venn.src.js 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998
  1. /**
  2. * @license Highcharts JS v9.0.1 (2021-02-16)
  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;
  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 _a;
  622. var component = this,
  623. graphic = component.graphic,
  624. animatableAttribs = params.animatableAttribs,
  625. onComplete = params.onComplete,
  626. css = params.css,
  627. renderer = params.renderer,
  628. animation = (_a = component.series) === null || _a === void 0 ? void 0 : _a.options.animation;
  629. if (component.shouldDraw()) {
  630. if (!graphic) {
  631. component.graphic = graphic =
  632. renderer[params.shapeType](params.shapeArgs)
  633. .add(params.group);
  634. }
  635. graphic
  636. .css(css)
  637. .attr(params.attribs)
  638. .animate(animatableAttribs, params.isNew ? false : animation, onComplete);
  639. }
  640. else if (graphic) {
  641. var destroy = function () {
  642. component.graphic = graphic = graphic.destroy();
  643. if (isFn(onComplete)) {
  644. onComplete();
  645. }
  646. };
  647. // animate only runs complete callback if something was animated.
  648. if (Object.keys(animatableAttribs).length) {
  649. graphic.animate(animatableAttribs, void 0, function () {
  650. destroy();
  651. });
  652. }
  653. else {
  654. destroy();
  655. }
  656. }
  657. };
  658. /**
  659. * An extended version of draw customized for points.
  660. * It calls additional methods that is expected when rendering a point.
  661. * @private
  662. * @param {Highcharts.Dictionary<any>} params Parameters
  663. */
  664. var drawPoint = function drawPoint(params) {
  665. var point = this,
  666. attribs = params.attribs = params.attribs || {};
  667. // Assigning class in dot notation does go well in IE8
  668. // eslint-disable-next-line dot-notation
  669. attribs['class'] = point.getClassName();
  670. // Call draw to render component
  671. draw.call(point, params);
  672. };
  673. var drawPointModule = {
  674. draw: draw,
  675. drawPoint: drawPoint,
  676. isFn: isFn
  677. };
  678. return drawPointModule;
  679. });
  680. _registerModule(_modules, 'Series/Venn/VennPoint.js', [_modules['Mixins/DrawPoint.js'], _modules['Core/Series/SeriesRegistry.js'], _modules['Core/Utilities.js']], function (DrawPointMixin, SeriesRegistry, U) {
  681. /* *
  682. *
  683. * Imports
  684. *
  685. * */
  686. var __extends = (this && this.__extends) || (function () {
  687. var extendStatics = function (d,
  688. b) {
  689. extendStatics = Object.setPrototypeOf ||
  690. ({ __proto__: [] } instanceof Array && function (d,
  691. b) { d.__proto__ = b; }) ||
  692. function (d,
  693. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  694. return extendStatics(d, b);
  695. };
  696. return function (d, b) {
  697. extendStatics(d, b);
  698. function __() { this.constructor = d; }
  699. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  700. };
  701. })();
  702. var ScatterSeries = SeriesRegistry.seriesTypes.scatter;
  703. var extend = U.extend,
  704. isNumber = U.isNumber;
  705. /* *
  706. *
  707. * Class
  708. *
  709. * */
  710. var VennPoint = /** @class */ (function (_super) {
  711. __extends(VennPoint, _super);
  712. function VennPoint() {
  713. /* *
  714. *
  715. * Properties
  716. *
  717. * */
  718. var _this = _super !== null && _super.apply(this,
  719. arguments) || this;
  720. _this.options = void 0;
  721. _this.series = void 0;
  722. return _this;
  723. /* eslint-enable valid-jsdoc */
  724. }
  725. /* *
  726. *
  727. * Functions
  728. *
  729. * */
  730. /* eslint-disable valid-jsdoc */
  731. VennPoint.prototype.isValid = function () {
  732. return isNumber(this.value);
  733. };
  734. VennPoint.prototype.shouldDraw = function () {
  735. var point = this;
  736. // Only draw points with single sets.
  737. return !!point.shapeArgs;
  738. };
  739. return VennPoint;
  740. }(ScatterSeries.prototype.pointClass));
  741. extend(VennPoint.prototype, {
  742. draw: DrawPointMixin.drawPoint
  743. });
  744. /* *
  745. *
  746. * Default Export
  747. *
  748. * */
  749. return VennPoint;
  750. });
  751. _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) {
  752. /* *
  753. *
  754. * Experimental Highcharts module which enables visualization of a Venn
  755. * diagram.
  756. *
  757. * (c) 2016-2021 Highsoft AS
  758. * Authors: Jon Arild Nygard
  759. *
  760. * Layout algorithm by Ben Frederickson:
  761. * https://www.benfrederickson.com/better-venn-diagrams/
  762. *
  763. * License: www.highcharts.com/license
  764. *
  765. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  766. *
  767. * */
  768. var getAreaOfCircle = GeometryCirclesModule.getAreaOfCircle,
  769. getCircleCircleIntersection = GeometryCirclesModule.getCircleCircleIntersection,
  770. getOverlapBetweenCirclesByDistance = GeometryCirclesModule.getOverlapBetweenCircles,
  771. isPointInsideAllCircles = GeometryCirclesModule.isPointInsideAllCircles,
  772. isPointInsideCircle = GeometryCirclesModule.isPointInsideCircle,
  773. isPointOutsideAllCircles = GeometryCirclesModule.isPointOutsideAllCircles;
  774. var getDistanceBetweenPoints = GeometryMixin.getDistanceBetweenPoints;
  775. var extend = U.extend,
  776. isArray = U.isArray,
  777. isNumber = U.isNumber,
  778. isObject = U.isObject,
  779. isString = U.isString;
  780. /* *
  781. *
  782. * Namespace
  783. *
  784. * */
  785. var VennUtils;
  786. (function (VennUtils) {
  787. /* *
  788. *
  789. * Properties
  790. *
  791. * */
  792. VennUtils.geometry = GeometryMixin;
  793. VennUtils.geometryCircles = GeometryCirclesModule;
  794. VennUtils.nelderMead = NelderMeadMixin;
  795. /* *
  796. *
  797. * Functions
  798. *
  799. * */
  800. /**
  801. * Takes an array of relations and adds the properties `totalOverlap` and
  802. * `overlapping` to each set. The property `totalOverlap` is the sum of
  803. * value for each relation where this set is included. The property
  804. * `overlapping` is a map of how much this set is overlapping another set.
  805. * NOTE: This algorithm ignores relations consisting of more than 2 sets.
  806. * @private
  807. * @param {Array<Highcharts.VennRelationObject>} relations
  808. * The list of relations that should be sorted.
  809. * @return {Array<Highcharts.VennRelationObject>}
  810. * Returns the modified input relations with added properties `totalOverlap`
  811. * and `overlapping`.
  812. */
  813. function addOverlapToSets(relations) {
  814. // Calculate the amount of overlap per set.
  815. var mapOfIdToProps = relations
  816. // Filter out relations consisting of 2 sets.
  817. .filter(function (relation) {
  818. return relation.sets.length === 2;
  819. })
  820. // Sum up the amount of overlap for each set.
  821. .reduce(function (map, relation) {
  822. var sets = relation.sets;
  823. sets.forEach(function (set, i, arr) {
  824. if (!isObject(map[set])) {
  825. map[set] = {
  826. overlapping: {},
  827. totalOverlap: 0
  828. };
  829. }
  830. map[set].totalOverlap += relation.value;
  831. map[set].overlapping[arr[1 - i]] = relation.value;
  832. });
  833. return map;
  834. }, {});
  835. relations
  836. // Filter out single sets
  837. .filter(isSet)
  838. // Extend the set with the calculated properties.
  839. .forEach(function (set) {
  840. var properties = mapOfIdToProps[set.sets[0]];
  841. extend(set, properties);
  842. });
  843. // Returns the modified relations.
  844. return relations;
  845. }
  846. VennUtils.addOverlapToSets = addOverlapToSets;
  847. /**
  848. * Finds the root of a given function. The root is the input value needed
  849. * for a function to return 0.
  850. *
  851. * See https://en.wikipedia.org/wiki/Bisection_method#Algorithm
  852. *
  853. * TODO: Add unit tests.
  854. *
  855. * @param {Function} f
  856. * The function to find the root of.
  857. * @param {number} a
  858. * The lowest number in the search range.
  859. * @param {number} b
  860. * The highest number in the search range.
  861. * @param {number} [tolerance=1e-10]
  862. * The allowed difference between the returned value and root.
  863. * @param {number} [maxIterations=100]
  864. * The maximum iterations allowed.
  865. * @return {number}
  866. * Root number.
  867. */
  868. function bisect(f, a, b, tolerance, maxIterations) {
  869. var fA = f(a),
  870. fB = f(b),
  871. nMax = maxIterations || 100,
  872. tol = tolerance || 1e-10,
  873. delta = b - a,
  874. n = 1,
  875. x,
  876. fX;
  877. if (a >= b) {
  878. throw new Error('a must be smaller than b.');
  879. }
  880. else if (fA * fB > 0) {
  881. throw new Error('f(a) and f(b) must have opposite signs.');
  882. }
  883. if (fA === 0) {
  884. x = a;
  885. }
  886. else if (fB === 0) {
  887. x = b;
  888. }
  889. else {
  890. while (n++ <= nMax && fX !== 0 && delta > tol) {
  891. delta = (b - a) / 2;
  892. x = a + delta;
  893. fX = f(x);
  894. // Update low and high for next search interval.
  895. if (fA * fX > 0) {
  896. a = x;
  897. }
  898. else {
  899. b = x;
  900. }
  901. }
  902. }
  903. return x;
  904. }
  905. /**
  906. * Uses the bisection method to make a best guess of the ideal distance
  907. * between two circles too get the desired overlap.
  908. * Currently there is no known formula to calculate the distance from the
  909. * area of overlap, which makes the bisection method preferred.
  910. * @private
  911. * @param {number} r1
  912. * Radius of the first circle.
  913. * @param {number} r2
  914. * Radiues of the second circle.
  915. * @param {number} overlap
  916. * The wanted overlap between the two circles.
  917. * @return {number}
  918. * Returns the distance needed to get the wanted overlap between the two
  919. * circles.
  920. */
  921. function getDistanceBetweenCirclesByOverlap(r1, r2, overlap) {
  922. var maxDistance = r1 + r2,
  923. distance;
  924. if (overlap <= 0) {
  925. // If overlap is below or equal to zero, then there is no overlap.
  926. distance = maxDistance;
  927. }
  928. else if (getAreaOfCircle(r1 < r2 ? r1 : r2) <= overlap) {
  929. // When area of overlap is larger than the area of the smallest
  930. // circle, then it is completely overlapping.
  931. distance = 0;
  932. }
  933. else {
  934. distance = bisect(function (x) {
  935. var actualOverlap = getOverlapBetweenCirclesByDistance(r1,
  936. r2,
  937. x);
  938. // Return the differance between wanted and actual overlap.
  939. return overlap - actualOverlap;
  940. }, 0, maxDistance);
  941. }
  942. return distance;
  943. }
  944. VennUtils.getDistanceBetweenCirclesByOverlap = getDistanceBetweenCirclesByOverlap;
  945. /**
  946. * Finds the available width for a label, by taking the label position and
  947. * finding the largest distance, which is inside all internal circles, and
  948. * outside all external circles.
  949. *
  950. * @private
  951. * @param {Highcharts.PositionObject} pos
  952. * The x and y coordinate of the label.
  953. * @param {Array<Highcharts.CircleObject>} internal
  954. * Internal circles.
  955. * @param {Array<Highcharts.CircleObject>} external
  956. * External circles.
  957. * @return {number}
  958. * Returns available width for the label.
  959. */
  960. function getLabelWidth(pos, internal, external) {
  961. var radius = internal.reduce(function (min,
  962. circle) {
  963. return Math.min(circle.r,
  964. min);
  965. }, Infinity),
  966. // Filter out external circles that are completely overlapping.
  967. filteredExternals = external.filter(function (circle) {
  968. return !isPointInsideCircle(pos, circle);
  969. });
  970. var findDistance = function (maxDistance,
  971. direction) {
  972. return bisect(function (x) {
  973. var testPos = {
  974. x: pos.x + (direction * x),
  975. y: pos.y
  976. },
  977. isValid = (isPointInsideAllCircles(testPos,
  978. internal) &&
  979. isPointOutsideAllCircles(testPos,
  980. filteredExternals));
  981. // If the position is valid, then we want to move towards the
  982. // max distance. If not, then we want to away from the max
  983. // distance.
  984. return -(maxDistance - x) + (isValid ? 0 : Number.MAX_VALUE);
  985. }, 0, maxDistance);
  986. };
  987. // Find the smallest distance of left and right.
  988. return Math.min(findDistance(radius, -1), findDistance(radius, 1)) * 2;
  989. }
  990. VennUtils.getLabelWidth = getLabelWidth;
  991. /**
  992. * Calculates a margin for a point based on the iternal and external
  993. * circles. The margin describes if the point is well placed within the
  994. * internal circles, and away from the external.
  995. * @private
  996. * @todo add unit tests.
  997. * @param {Highcharts.PositionObject} point
  998. * The point to evaluate.
  999. * @param {Array<Highcharts.CircleObject>} internal
  1000. * The internal circles.
  1001. * @param {Array<Highcharts.CircleObject>} external
  1002. * The external circles.
  1003. * @return {number}
  1004. * Returns the margin.
  1005. */
  1006. function getMarginFromCircles(point, internal, external) {
  1007. var margin = internal.reduce(function (margin,
  1008. circle) {
  1009. var m = circle.r - getDistanceBetweenPoints(point,
  1010. circle);
  1011. return (m <= margin) ? m : margin;
  1012. }, Number.MAX_VALUE);
  1013. margin = external.reduce(function (margin, circle) {
  1014. var m = getDistanceBetweenPoints(point,
  1015. circle) - circle.r;
  1016. return (m <= margin) ? m : margin;
  1017. }, margin);
  1018. return margin;
  1019. }
  1020. VennUtils.getMarginFromCircles = getMarginFromCircles;
  1021. /**
  1022. * Calculates the area of overlap between a list of circles.
  1023. * @private
  1024. * @todo add support for calculating overlap between more than 2 circles.
  1025. * @param {Array<Highcharts.CircleObject>} circles
  1026. * List of circles with their given positions.
  1027. * @return {number}
  1028. * Returns the area of overlap between all the circles.
  1029. */
  1030. function getOverlapBetweenCircles(circles) {
  1031. var overlap = 0;
  1032. // When there is only two circles we can find the overlap by using their
  1033. // radiuses and the distance between them.
  1034. if (circles.length === 2) {
  1035. var circle1 = circles[0];
  1036. var circle2 = circles[1];
  1037. overlap = getOverlapBetweenCirclesByDistance(circle1.r, circle2.r, getDistanceBetweenPoints(circle1, circle2));
  1038. }
  1039. return overlap;
  1040. }
  1041. // eslint-disable-next-line require-jsdoc
  1042. function isSet(x) {
  1043. return isArray(x.sets) && x.sets.length === 1;
  1044. }
  1045. VennUtils.isSet = isSet;
  1046. // eslint-disable-next-line require-jsdoc
  1047. function isValidRelation(x) {
  1048. var map = {};
  1049. return (isObject(x) &&
  1050. (isNumber(x.value) && x.value > -1) &&
  1051. (isArray(x.sets) && x.sets.length > 0) &&
  1052. !x.sets.some(function (set) {
  1053. var invalid = false;
  1054. if (!map[set] && isString(set)) {
  1055. map[set] = true;
  1056. }
  1057. else {
  1058. invalid = true;
  1059. }
  1060. return invalid;
  1061. }));
  1062. }
  1063. // eslint-disable-next-line require-jsdoc
  1064. function isValidSet(x) {
  1065. return (isValidRelation(x) && isSet(x) && x.value > 0);
  1066. }
  1067. /**
  1068. * Uses a greedy approach to position all the sets. Works well with a small
  1069. * number of sets, and are in these cases a good choice aesthetically.
  1070. * @private
  1071. * @param {Array<object>} relations List of the overlap between two or more
  1072. * sets, or the size of a single set.
  1073. * @return {Array<object>} List of circles and their calculated positions.
  1074. */
  1075. function layoutGreedyVenn(relations) {
  1076. var positionedSets = [],
  1077. mapOfIdToCircles = {};
  1078. // Define a circle for each set.
  1079. relations
  1080. .filter(function (relation) {
  1081. return relation.sets.length === 1;
  1082. }).forEach(function (relation) {
  1083. mapOfIdToCircles[relation.sets[0]] = relation.circle = {
  1084. x: Number.MAX_VALUE,
  1085. y: Number.MAX_VALUE,
  1086. r: Math.sqrt(relation.value / Math.PI)
  1087. };
  1088. });
  1089. /**
  1090. * Takes a set and updates the position, and add the set to the list of
  1091. * positioned sets.
  1092. * @private
  1093. * @param {object} set
  1094. * The set to add to its final position.
  1095. * @param {object} coordinates
  1096. * The coordinates to position the set at.
  1097. * @return {void}
  1098. */
  1099. var positionSet = function positionSet(set,
  1100. coordinates) {
  1101. var circle = set.circle;
  1102. circle.x = coordinates.x;
  1103. circle.y = coordinates.y;
  1104. positionedSets.push(set);
  1105. };
  1106. // Find overlap between sets. Ignore relations with more then 2 sets.
  1107. addOverlapToSets(relations);
  1108. // Sort sets by the sum of their size from large to small.
  1109. var sortedByOverlap = relations
  1110. .filter(isSet)
  1111. .sort(sortByTotalOverlap);
  1112. // Position the most overlapped set at 0,0.
  1113. positionSet(sortedByOverlap.shift(), { x: 0, y: 0 });
  1114. var relationsWithTwoSets = relations.filter(function (x) {
  1115. return x.sets.length === 2;
  1116. });
  1117. // Iterate and position the remaining sets.
  1118. sortedByOverlap.forEach(function (set) {
  1119. var circle = set.circle,
  1120. radius = circle.r,
  1121. overlapping = set.overlapping;
  1122. var bestPosition = positionedSets
  1123. .reduce(function (best,
  1124. positionedSet,
  1125. i) {
  1126. var positionedCircle = positionedSet.circle,
  1127. overlap = overlapping[positionedSet.sets[0]];
  1128. // Calculate the distance between the sets to get the
  1129. // correct overlap
  1130. var distance = getDistanceBetweenCirclesByOverlap(radius,
  1131. positionedCircle.r,
  1132. overlap);
  1133. // Create a list of possible coordinates calculated from
  1134. // distance.
  1135. var possibleCoordinates = [
  1136. { x: positionedCircle.x + distance,
  1137. y: positionedCircle.y },
  1138. { x: positionedCircle.x - distance,
  1139. y: positionedCircle.y },
  1140. { x: positionedCircle.x,
  1141. y: positionedCircle.y + distance },
  1142. { x: positionedCircle.x,
  1143. y: positionedCircle.y - distance }
  1144. ];
  1145. // If there are more circles overlapping, then add the
  1146. // intersection points as possible positions.
  1147. positionedSets.slice(i + 1).forEach(function (positionedSet2) {
  1148. var positionedCircle2 = positionedSet2.circle,
  1149. overlap2 = overlapping[positionedSet2.sets[0]],
  1150. distance2 = getDistanceBetweenCirclesByOverlap(radius,
  1151. positionedCircle2.r,
  1152. overlap2);
  1153. // Add intersections to list of coordinates.
  1154. possibleCoordinates = possibleCoordinates.concat(getCircleCircleIntersection({
  1155. x: positionedCircle.x,
  1156. y: positionedCircle.y,
  1157. r: distance
  1158. }, {
  1159. x: positionedCircle2.x,
  1160. y: positionedCircle2.y,
  1161. r: distance2
  1162. }));
  1163. });
  1164. // Iterate all suggested coordinates and find the best one.
  1165. possibleCoordinates.forEach(function (coordinates) {
  1166. circle.x = coordinates.x;
  1167. circle.y = coordinates.y;
  1168. // Calculate loss for the suggested coordinates.
  1169. var currentLoss = loss(mapOfIdToCircles,
  1170. relationsWithTwoSets);
  1171. // If the loss is better, then use these new coordinates
  1172. if (currentLoss < best.loss) {
  1173. best.loss = currentLoss;
  1174. best.coordinates = coordinates;
  1175. }
  1176. });
  1177. // Return resulting coordinates.
  1178. return best;
  1179. }, {
  1180. loss: Number.MAX_VALUE,
  1181. coordinates: void 0
  1182. });
  1183. // Add the set to its final position.
  1184. positionSet(set, bestPosition.coordinates);
  1185. });
  1186. // Return the positions of each set.
  1187. return mapOfIdToCircles;
  1188. }
  1189. VennUtils.layoutGreedyVenn = layoutGreedyVenn;
  1190. /**
  1191. * Calculates the difference between the desired overlap and the actual
  1192. * overlap between two circles.
  1193. * @private
  1194. * @param {Dictionary<Highcharts.CircleObject>} mapOfIdToCircle
  1195. * Map from id to circle.
  1196. * @param {Array<Highcharts.VennRelationObject>} relations
  1197. * List of relations to calculate the loss of.
  1198. * @return {number}
  1199. * Returns the loss between positions of the circles for the given
  1200. * relations.
  1201. */
  1202. function loss(mapOfIdToCircle, relations) {
  1203. var precision = 10e10;
  1204. // Iterate all the relations and calculate their individual loss.
  1205. return relations.reduce(function (totalLoss, relation) {
  1206. var loss = 0;
  1207. if (relation.sets.length > 1) {
  1208. var wantedOverlap = relation.value;
  1209. // Calculate the actual overlap between the sets.
  1210. var actualOverlap = getOverlapBetweenCircles(
  1211. // Get the circles for the given sets.
  1212. relation.sets.map(function (set) {
  1213. return mapOfIdToCircle[set];
  1214. }));
  1215. var diff = wantedOverlap - actualOverlap;
  1216. loss = Math.round((diff * diff) * precision) / precision;
  1217. }
  1218. // Add calculated loss to the sum.
  1219. return totalLoss + loss;
  1220. }, 0);
  1221. }
  1222. VennUtils.loss = loss;
  1223. /**
  1224. * Prepares the venn data so that it is usable for the layout function.
  1225. * Filter out sets, or intersections that includes sets, that are missing in
  1226. * the data or has (value < 1). Adds missing relations between sets in the
  1227. * data as value = 0.
  1228. * @private
  1229. * @param {Array<object>} data The raw input data.
  1230. * @return {Array<object>} Returns an array of valid venn data.
  1231. */
  1232. function processVennData(data) {
  1233. var d = isArray(data) ? data : [];
  1234. var validSets = d
  1235. .reduce(function (arr,
  1236. x) {
  1237. // Check if x is a valid set, and that it is not an duplicate.
  1238. if (isValidSet(x) && arr.indexOf(x.sets[0]) === -1) {
  1239. arr.push(x.sets[0]);
  1240. }
  1241. return arr;
  1242. }, [])
  1243. .sort();
  1244. var mapOfIdToRelation = d.reduce(function (mapOfIdToRelation,
  1245. relation) {
  1246. if (isValidRelation(relation) &&
  1247. !relation.sets.some(function (set) {
  1248. return validSets.indexOf(set) === -1;
  1249. })) {
  1250. mapOfIdToRelation[relation.sets.sort().join()] =
  1251. relation;
  1252. }
  1253. return mapOfIdToRelation;
  1254. }, {});
  1255. validSets.reduce(function (combinations, set, i, arr) {
  1256. var remaining = arr.slice(i + 1);
  1257. remaining.forEach(function (set2) {
  1258. combinations.push(set + ',' + set2);
  1259. });
  1260. return combinations;
  1261. }, []).forEach(function (combination) {
  1262. if (!mapOfIdToRelation[combination]) {
  1263. var obj = {
  1264. sets: combination.split(','),
  1265. value: 0
  1266. };
  1267. mapOfIdToRelation[combination] = obj;
  1268. }
  1269. });
  1270. // Transform map into array.
  1271. return Object
  1272. .keys(mapOfIdToRelation)
  1273. .map(function (id) {
  1274. return mapOfIdToRelation[id];
  1275. });
  1276. }
  1277. VennUtils.processVennData = processVennData;
  1278. /**
  1279. * Takes two sets and finds the one with the largest total overlap.
  1280. * @private
  1281. * @param {object} a The first set to compare.
  1282. * @param {object} b The second set to compare.
  1283. * @return {number} Returns 0 if a and b are equal, <0 if a is greater, >0 if b
  1284. * is greater.
  1285. */
  1286. function sortByTotalOverlap(a, b) {
  1287. return b.totalOverlap - a.totalOverlap;
  1288. }
  1289. VennUtils.sortByTotalOverlap = sortByTotalOverlap;
  1290. })(VennUtils || (VennUtils = {}));
  1291. /* *
  1292. *
  1293. * Default Export
  1294. *
  1295. * */
  1296. return VennUtils;
  1297. });
  1298. _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) {
  1299. /* *
  1300. *
  1301. * Experimental Highcharts module which enables visualization of a Venn
  1302. * diagram.
  1303. *
  1304. * (c) 2016-2021 Highsoft AS
  1305. * Authors: Jon Arild Nygard
  1306. *
  1307. * Layout algorithm by Ben Frederickson:
  1308. * https://www.benfrederickson.com/better-venn-diagrams/
  1309. *
  1310. * License: www.highcharts.com/license
  1311. *
  1312. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1313. *
  1314. * */
  1315. var __extends = (this && this.__extends) || (function () {
  1316. var extendStatics = function (d,
  1317. b) {
  1318. extendStatics = Object.setPrototypeOf ||
  1319. ({ __proto__: [] } instanceof Array && function (d,
  1320. b) { d.__proto__ = b; }) ||
  1321. function (d,
  1322. b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  1323. return extendStatics(d, b);
  1324. };
  1325. return function (d, b) {
  1326. extendStatics(d, b);
  1327. function __() { this.constructor = d; }
  1328. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  1329. };
  1330. })();
  1331. var animObject = A.animObject;
  1332. var color = Color.parse;
  1333. var getCenterOfPoints = GeometryMixin.getCenterOfPoints,
  1334. getDistanceBetweenPoints = GeometryMixin.getDistanceBetweenPoints;
  1335. var getAreaOfCircle = GeometryCirclesModule.getAreaOfCircle,
  1336. getAreaOfIntersectionBetweenCircles = GeometryCirclesModule.getAreaOfIntersectionBetweenCircles,
  1337. getCircleCircleIntersection = GeometryCirclesModule.getCircleCircleIntersection,
  1338. getCirclesIntersectionPolygon = GeometryCirclesModule.getCirclesIntersectionPolygon,
  1339. getOverlapBetweenCirclesByDistance = GeometryCirclesModule.getOverlapBetweenCircles,
  1340. isCircle1CompletelyOverlappingCircle2 = GeometryCirclesModule.isCircle1CompletelyOverlappingCircle2,
  1341. isPointInsideAllCircles = GeometryCirclesModule.isPointInsideAllCircles,
  1342. isPointInsideCircle = GeometryCirclesModule.isPointInsideCircle,
  1343. isPointOutsideAllCircles = GeometryCirclesModule.isPointOutsideAllCircles;
  1344. var nelderMead = NelderMeadMixin.nelderMead;
  1345. var ScatterSeries = SeriesRegistry.seriesTypes.scatter;
  1346. var addEvent = U.addEvent,
  1347. extend = U.extend,
  1348. isArray = U.isArray,
  1349. isNumber = U.isNumber,
  1350. isObject = U.isObject,
  1351. isString = U.isString,
  1352. merge = U.merge;
  1353. /* *
  1354. *
  1355. * Class
  1356. *
  1357. * */
  1358. /**
  1359. * @private
  1360. * @class
  1361. * @name Highcharts.seriesTypes.venn
  1362. *
  1363. * @augments Highcharts.Series
  1364. */
  1365. var VennSeries = /** @class */ (function (_super) {
  1366. __extends(VennSeries, _super);
  1367. function VennSeries() {
  1368. /* *
  1369. *
  1370. * Static Properties
  1371. *
  1372. * */
  1373. var _this = _super !== null && _super.apply(this,
  1374. arguments) || this;
  1375. /* *
  1376. *
  1377. * Properties
  1378. *
  1379. * */
  1380. _this.data = void 0;
  1381. _this.mapOfIdToRelation = void 0;
  1382. _this.options = void 0;
  1383. _this.points = void 0;
  1384. return _this;
  1385. /* eslint-enable valid-jsdoc */
  1386. }
  1387. /* *
  1388. *
  1389. * Static Functions
  1390. *
  1391. * */
  1392. /**
  1393. * Finds the optimal label position by looking for a position that has a low
  1394. * distance from the internal circles, and as large possible distane to the
  1395. * external circles.
  1396. * @private
  1397. * @todo Optimize the intial position.
  1398. * @todo Add unit tests.
  1399. * @param {Array<Highcharts.CircleObject>} internal
  1400. * Internal circles.
  1401. * @param {Array<Highcharts.CircleObject>} external
  1402. * External circles.
  1403. * @return {Highcharts.PositionObject}
  1404. * Returns the found position.
  1405. */
  1406. VennSeries.getLabelPosition = function (internal, external) {
  1407. // Get the best label position within the internal circles.
  1408. var best = internal.reduce(function (best,
  1409. circle) {
  1410. var d = circle.r / 2;
  1411. // Give a set of points with the circle to evaluate as the best
  1412. // label position.
  1413. return [
  1414. { x: circle.x, y: circle.y },
  1415. { x: circle.x + d, y: circle.y },
  1416. { x: circle.x - d, y: circle.y },
  1417. { x: circle.x, y: circle.y + d },
  1418. { x: circle.x, y: circle.y - d }
  1419. ]
  1420. // Iterate the given points and return the one with the largest
  1421. // margin.
  1422. .reduce(function (best, point) {
  1423. var margin = VennUtils.getMarginFromCircles(point,
  1424. internal,
  1425. external);
  1426. // If the margin better than the current best, then update
  1427. // sbest.
  1428. if (best.margin < margin) {
  1429. best.point = point;
  1430. best.margin = margin;
  1431. }
  1432. return best;
  1433. }, best);
  1434. }, {
  1435. point: void 0,
  1436. margin: -Number.MAX_VALUE
  1437. }).point;
  1438. // Use nelder mead to optimize the initial label position.
  1439. var optimal = nelderMead(function (p) {
  1440. return -(VennUtils.getMarginFromCircles({ x: p[0],
  1441. y: p[1] },
  1442. internal,
  1443. external));
  1444. }, [best.x, best.y]);
  1445. // Update best to be the point which was found to have the best margin.
  1446. best = {
  1447. x: optimal[0],
  1448. y: optimal[1]
  1449. };
  1450. if (!(isPointInsideAllCircles(best, internal) &&
  1451. isPointOutsideAllCircles(best, external))) {
  1452. // If point was either outside one of the internal, or inside one of
  1453. // the external, then it was invalid and should use a fallback.
  1454. if (internal.length > 1) {
  1455. best = getCenterOfPoints(getCirclesIntersectionPolygon(internal));
  1456. }
  1457. else {
  1458. best = {
  1459. x: internal[0].x,
  1460. y: internal[0].y
  1461. };
  1462. }
  1463. }
  1464. // Return the best point.
  1465. return best;
  1466. };
  1467. /**
  1468. * Calulates data label values for a given relations object.
  1469. *
  1470. * @private
  1471. * @todo add unit tests
  1472. * @param {Highcharts.VennRelationObject} relation A relations object.
  1473. * @param {Array<Highcharts.VennRelationObject>} setRelations The list of
  1474. * relations that is a set.
  1475. * @return {Highcharts.VennLabelValuesObject}
  1476. * Returns an object containing position and width of the label.
  1477. */
  1478. VennSeries.getLabelValues = function (relation, setRelations) {
  1479. var sets = relation.sets;
  1480. // Create a list of internal and external circles.
  1481. var data = setRelations.reduce(function (data,
  1482. set) {
  1483. // If the set exists in this relation, then it is internal,
  1484. // otherwise it will be external.
  1485. var isInternal = sets.indexOf(set.sets[0]) > -1;
  1486. var property = isInternal ? 'internal' : 'external';
  1487. // Add the circle to the list.
  1488. data[property].push(set.circle);
  1489. return data;
  1490. }, {
  1491. internal: [],
  1492. external: []
  1493. });
  1494. // Filter out external circles that are completely overlapping all
  1495. // internal
  1496. data.external = data.external.filter(function (externalCircle) {
  1497. return data.internal.some(function (internalCircle) {
  1498. return !isCircle1CompletelyOverlappingCircle2(externalCircle, internalCircle);
  1499. });
  1500. });
  1501. // Calulate the label position.
  1502. var position = VennSeries.getLabelPosition(data.internal,
  1503. data.external);
  1504. // Calculate the label width
  1505. var width = VennUtils.getLabelWidth(position,
  1506. data.internal,
  1507. data.external);
  1508. return {
  1509. position: position,
  1510. width: width
  1511. };
  1512. };
  1513. /**
  1514. * Calculates the positions, and the label values of all the sets in the
  1515. * venn diagram.
  1516. *
  1517. * @private
  1518. * @todo Add support for constrained MDS.
  1519. * @param {Array<Highchats.VennRelationObject>} relations
  1520. * List of the overlap between two or more sets, or the size of a single
  1521. * sset.
  1522. * @return {Highcharts.Dictionary<*>}
  1523. * List of circles and their calculated positions.
  1524. */
  1525. VennSeries.layout = function (relations) {
  1526. var mapOfIdToShape = {};
  1527. var mapOfIdToLabelValues = {};
  1528. // Calculate best initial positions by using greedy layout.
  1529. if (relations.length > 0) {
  1530. var mapOfIdToCircles_1 = VennUtils.layoutGreedyVenn(relations);
  1531. var setRelations_1 = relations.filter(VennUtils.isSet);
  1532. relations
  1533. .forEach(function (relation) {
  1534. var sets = relation.sets;
  1535. var id = sets.join();
  1536. // Get shape from map of circles, or calculate intersection.
  1537. var shape = VennUtils.isSet(relation) ?
  1538. mapOfIdToCircles_1[id] :
  1539. getAreaOfIntersectionBetweenCircles(sets.map(function (set) {
  1540. return mapOfIdToCircles_1[set];
  1541. }));
  1542. // Calculate label values if the set has a shape
  1543. if (shape) {
  1544. mapOfIdToShape[id] = shape;
  1545. mapOfIdToLabelValues[id] = VennSeries.getLabelValues(relation, setRelations_1);
  1546. }
  1547. });
  1548. }
  1549. return { mapOfIdToShape: mapOfIdToShape, mapOfIdToLabelValues: mapOfIdToLabelValues };
  1550. };
  1551. /**
  1552. * Calculates the proper scale to fit the cloud inside the plotting area.
  1553. * @private
  1554. * @todo add unit test
  1555. * @param {number} targetWidth
  1556. * Width of target area.
  1557. * @param {number} targetHeight
  1558. * Height of target area.
  1559. * @param {Highcharts.PolygonBoxObject} field
  1560. * The playing field.
  1561. * @return {Highcharts.Dictionary<number>}
  1562. * Returns the value to scale the playing field up to the size of the target
  1563. * area, and center of x and y.
  1564. */
  1565. VennSeries.getScale = function (targetWidth, targetHeight, field) {
  1566. var height = field.bottom - field.top, // top is smaller than bottom
  1567. width = field.right - field.left,
  1568. scaleX = width > 0 ? 1 / width * targetWidth : 1,
  1569. scaleY = height > 0 ? 1 / height * targetHeight : 1,
  1570. adjustX = (field.right + field.left) / 2,
  1571. adjustY = (field.top + field.bottom) / 2,
  1572. scale = Math.min(scaleX,
  1573. scaleY);
  1574. return {
  1575. scale: scale,
  1576. centerX: targetWidth / 2 - adjustX * scale,
  1577. centerY: targetHeight / 2 - adjustY * scale
  1578. };
  1579. };
  1580. /**
  1581. * If a circle is outside a give field, then the boundaries of the field is
  1582. * adjusted accordingly. Modifies the field object which is passed as the
  1583. * first parameter.
  1584. * @private
  1585. * @todo NOTE: Copied from wordcloud, can probably be unified.
  1586. * @param {Highcharts.PolygonBoxObject} field
  1587. * The bounding box of a playing field.
  1588. * @param {Highcharts.CircleObject} circle
  1589. * The bounding box for a placed point.
  1590. * @return {Highcharts.PolygonBoxObject}
  1591. * Returns a modified field object.
  1592. */
  1593. VennSeries.updateFieldBoundaries = function (field, circle) {
  1594. var left = circle.x - circle.r,
  1595. right = circle.x + circle.r,
  1596. bottom = circle.y + circle.r,
  1597. top = circle.y - circle.r;
  1598. // TODO improve type checking.
  1599. if (!isNumber(field.left) || field.left > left) {
  1600. field.left = left;
  1601. }
  1602. if (!isNumber(field.right) || field.right < right) {
  1603. field.right = right;
  1604. }
  1605. if (!isNumber(field.top) || field.top > top) {
  1606. field.top = top;
  1607. }
  1608. if (!isNumber(field.bottom) || field.bottom < bottom) {
  1609. field.bottom = bottom;
  1610. }
  1611. return field;
  1612. };
  1613. /* *
  1614. *
  1615. * Functions
  1616. *
  1617. * */
  1618. /* eslint-disable valid-jsdoc */
  1619. VennSeries.prototype.animate = function (init) {
  1620. if (!init) {
  1621. var series = this,
  1622. animOptions = animObject(series.options.animation);
  1623. series.points.forEach(function (point) {
  1624. var args = point.shapeArgs;
  1625. if (point.graphic && args) {
  1626. var attr = {},
  1627. animate = {};
  1628. if (args.d) {
  1629. // If shape is a path, then animate opacity.
  1630. attr.opacity = 0.001;
  1631. }
  1632. else {
  1633. // If shape is a circle, then animate radius.
  1634. attr.r = 0;
  1635. animate.r = args.r;
  1636. }
  1637. point.graphic
  1638. .attr(attr)
  1639. .animate(animate, animOptions);
  1640. // If shape is path, then fade it in after the circles
  1641. // animation
  1642. if (args.d) {
  1643. setTimeout(function () {
  1644. if (point && point.graphic) {
  1645. point.graphic.animate({
  1646. opacity: 1
  1647. });
  1648. }
  1649. }, animOptions.duration);
  1650. }
  1651. }
  1652. }, series);
  1653. }
  1654. };
  1655. /**
  1656. * Draw the graphics for each point.
  1657. * @private
  1658. */
  1659. VennSeries.prototype.drawPoints = function () {
  1660. var series = this,
  1661. // Series properties
  1662. chart = series.chart,
  1663. group = series.group,
  1664. points = series.points || [],
  1665. // Chart properties
  1666. renderer = chart.renderer;
  1667. // Iterate all points and calculate and draw their graphics.
  1668. points.forEach(function (point) {
  1669. var attribs = {
  1670. zIndex: isArray(point.sets) ? point.sets.length : 0
  1671. },
  1672. shapeArgs = point.shapeArgs;
  1673. // Add point attribs
  1674. if (!chart.styledMode) {
  1675. extend(attribs, series.pointAttribs(point, point.state));
  1676. }
  1677. // Draw the point graphic.
  1678. point.draw({
  1679. isNew: !point.graphic,
  1680. animatableAttribs: shapeArgs,
  1681. attribs: attribs,
  1682. group: group,
  1683. renderer: renderer,
  1684. shapeType: shapeArgs && shapeArgs.d ? 'path' : 'circle'
  1685. });
  1686. });
  1687. };
  1688. VennSeries.prototype.init = function () {
  1689. ScatterSeries.prototype.init.apply(this, arguments);
  1690. // Venn's opacity is a different option from other series
  1691. delete this.opacity;
  1692. };
  1693. /**
  1694. * Calculates the style attributes for a point. The attributes can vary
  1695. * depending on the state of the point.
  1696. * @private
  1697. * @param {Highcharts.Point} point
  1698. * The point which will get the resulting attributes.
  1699. * @param {string} [state]
  1700. * The state of the point.
  1701. * @return {Highcharts.SVGAttributes}
  1702. * Returns the calculated attributes.
  1703. */
  1704. VennSeries.prototype.pointAttribs = function (point, state) {
  1705. var series = this,
  1706. seriesOptions = series.options || {},
  1707. pointOptions = point && point.options || {},
  1708. stateOptions = (state && seriesOptions.states[state]) || {},
  1709. options = merge(seriesOptions, { color: point && point.color },
  1710. pointOptions,
  1711. stateOptions);
  1712. // Return resulting values for the attributes.
  1713. return {
  1714. 'fill': color(options.color)
  1715. .brighten(options.brightness)
  1716. .get(),
  1717. // Set opacity directly to the SVG element, not to pattern #14372.
  1718. opacity: options.opacity,
  1719. 'stroke': options.borderColor,
  1720. 'stroke-width': options.borderWidth,
  1721. 'dashstyle': options.borderDashStyle
  1722. };
  1723. };
  1724. VennSeries.prototype.translate = function () {
  1725. var chart = this.chart;
  1726. this.processedXData = this.xData;
  1727. this.generatePoints();
  1728. // Process the data before passing it into the layout function.
  1729. var relations = VennUtils.processVennData(this.options.data);
  1730. // Calculate the positions of each circle.
  1731. var _a = VennSeries.layout(relations),
  1732. mapOfIdToShape = _a.mapOfIdToShape,
  1733. mapOfIdToLabelValues = _a.mapOfIdToLabelValues;
  1734. // Calculate the scale, and center of the plot area.
  1735. var field = Object.keys(mapOfIdToShape)
  1736. .filter(function (key) {
  1737. var shape = mapOfIdToShape[key];
  1738. return shape && isNumber(shape.r);
  1739. })
  1740. .reduce(function (field, key) {
  1741. return VennSeries.updateFieldBoundaries(field, mapOfIdToShape[key]);
  1742. }, { 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;
  1743. // Iterate all points and calculate and draw their graphics.
  1744. this.points.forEach(function (point) {
  1745. var sets = isArray(point.sets) ? point.sets : [],
  1746. id = sets.join(),
  1747. shape = mapOfIdToShape[id],
  1748. shapeArgs,
  1749. dataLabelValues = mapOfIdToLabelValues[id] || {},
  1750. dataLabelWidth = dataLabelValues.width,
  1751. dataLabelPosition = dataLabelValues.position,
  1752. dlOptions = point.options && point.options.dataLabels;
  1753. if (shape) {
  1754. if (shape.r) {
  1755. shapeArgs = {
  1756. x: centerX + shape.x * scale,
  1757. y: centerY + shape.y * scale,
  1758. r: shape.r * scale
  1759. };
  1760. }
  1761. else if (shape.d) {
  1762. var d = shape.d;
  1763. d.forEach(function (seg) {
  1764. if (seg[0] === 'M') {
  1765. seg[1] = centerX + seg[1] * scale;
  1766. seg[2] = centerY + seg[2] * scale;
  1767. }
  1768. else if (seg[0] === 'A') {
  1769. seg[1] = seg[1] * scale;
  1770. seg[2] = seg[2] * scale;
  1771. seg[6] = centerX + seg[6] * scale;
  1772. seg[7] = centerY + seg[7] * scale;
  1773. }
  1774. });
  1775. shapeArgs = { d: d };
  1776. }
  1777. // Scale the position for the data label.
  1778. if (dataLabelPosition) {
  1779. dataLabelPosition.x = centerX + dataLabelPosition.x * scale;
  1780. dataLabelPosition.y = centerY + dataLabelPosition.y * scale;
  1781. }
  1782. else {
  1783. dataLabelPosition = {};
  1784. }
  1785. if (isNumber(dataLabelWidth)) {
  1786. dataLabelWidth = Math.round(dataLabelWidth * scale);
  1787. }
  1788. }
  1789. point.shapeArgs = shapeArgs;
  1790. // Placement for the data labels
  1791. if (dataLabelPosition && shapeArgs) {
  1792. point.plotX = dataLabelPosition.x;
  1793. point.plotY = dataLabelPosition.y;
  1794. }
  1795. // Add width for the data label
  1796. if (dataLabelWidth && shapeArgs) {
  1797. point.dlOptions = merge(true, {
  1798. style: {
  1799. width: dataLabelWidth
  1800. }
  1801. }, isObject(dlOptions) && dlOptions);
  1802. }
  1803. // Set name for usage in tooltip and in data label.
  1804. point.name = point.options.name || sets.join('∩');
  1805. });
  1806. };
  1807. /**
  1808. * A Venn diagram displays all possible logical relations between a
  1809. * collection of different sets. The sets are represented by circles, and
  1810. * the relation between the sets are displayed by the overlap or lack of
  1811. * overlap between them. The venn diagram is a special case of Euler
  1812. * diagrams, which can also be displayed by this series type.
  1813. *
  1814. * @sample {highcharts} highcharts/demo/venn-diagram/
  1815. * Venn diagram
  1816. * @sample {highcharts} highcharts/demo/euler-diagram/
  1817. * Euler diagram
  1818. *
  1819. * @extends plotOptions.scatter
  1820. * @excluding connectEnds, connectNulls, cropThreshold, dragDrop,
  1821. * findNearestPointBy, getExtremesFromAll, jitter, label,
  1822. * linecap, lineWidth, linkedTo, marker, negativeColor,
  1823. * pointInterval, pointIntervalUnit, pointPlacement,
  1824. * pointStart, softThreshold, stacking, steps, threshold,
  1825. * xAxis, yAxis, zoneAxis, zones, dataSorting, boostThreshold,
  1826. * boostBlending
  1827. * @product highcharts
  1828. * @requires modules/venn
  1829. * @optionparent plotOptions.venn
  1830. */
  1831. VennSeries.defaultOptions = merge(ScatterSeries.defaultOptions, {
  1832. borderColor: palette.neutralColor20,
  1833. borderDashStyle: 'solid',
  1834. borderWidth: 1,
  1835. brighten: 0,
  1836. clip: false,
  1837. colorByPoint: true,
  1838. dataLabels: {
  1839. enabled: true,
  1840. verticalAlign: 'middle',
  1841. formatter: function () {
  1842. return this.point.name;
  1843. }
  1844. },
  1845. /**
  1846. * @ignore-option
  1847. * @private
  1848. */
  1849. inactiveOtherPoints: true,
  1850. marker: false,
  1851. opacity: 0.75,
  1852. showInLegend: false,
  1853. states: {
  1854. /**
  1855. * @excluding halo
  1856. */
  1857. hover: {
  1858. opacity: 1,
  1859. borderColor: palette.neutralColor80
  1860. },
  1861. /**
  1862. * @excluding halo
  1863. */
  1864. select: {
  1865. color: palette.neutralColor20,
  1866. borderColor: palette.neutralColor100,
  1867. animation: false
  1868. },
  1869. inactive: {
  1870. opacity: 0.075
  1871. }
  1872. },
  1873. tooltip: {
  1874. pointFormat: '{point.name}: {point.value}'
  1875. }
  1876. });
  1877. return VennSeries;
  1878. }(ScatterSeries));
  1879. extend(VennSeries.prototype, {
  1880. axisTypes: [],
  1881. directTouch: true,
  1882. isCartesian: false,
  1883. pointArrayMap: ['value'],
  1884. pointClass: VennPoint,
  1885. utils: VennUtils
  1886. });
  1887. SeriesRegistry.registerSeriesType('venn', VennSeries);
  1888. /* *
  1889. *
  1890. * Default Export
  1891. *
  1892. * */
  1893. /* *
  1894. *
  1895. * API Options
  1896. *
  1897. * */
  1898. /**
  1899. * A `venn` series. If the [type](#series.venn.type) option is
  1900. * not specified, it is inherited from [chart.type](#chart.type).
  1901. *
  1902. * @extends series,plotOptions.venn
  1903. * @excluding connectEnds, connectNulls, cropThreshold, dataParser, dataURL,
  1904. * findNearestPointBy, getExtremesFromAll, label, linecap, lineWidth,
  1905. * linkedTo, marker, negativeColor, pointInterval, pointIntervalUnit,
  1906. * pointPlacement, pointStart, softThreshold, stack, stacking, steps,
  1907. * threshold, xAxis, yAxis, zoneAxis, zones, dataSorting,
  1908. * boostThreshold, boostBlending
  1909. * @product highcharts
  1910. * @requires modules/venn
  1911. * @apioption series.venn
  1912. */
  1913. /**
  1914. * @type {Array<*>}
  1915. * @extends series.scatter.data
  1916. * @excluding marker, x, y
  1917. * @product highcharts
  1918. * @apioption series.venn.data
  1919. */
  1920. /**
  1921. * The name of the point. Used in data labels and tooltip. If name is not
  1922. * defined then it will default to the joined values in
  1923. * [sets](#series.venn.sets).
  1924. *
  1925. * @sample {highcharts} highcharts/demo/venn-diagram/
  1926. * Venn diagram
  1927. * @sample {highcharts} highcharts/demo/euler-diagram/
  1928. * Euler diagram
  1929. *
  1930. * @type {number}
  1931. * @since 7.0.0
  1932. * @product highcharts
  1933. * @apioption series.venn.data.name
  1934. */
  1935. /**
  1936. * The value of the point, resulting in a relative area of the circle, or area
  1937. * of overlap between two sets in the venn or euler diagram.
  1938. *
  1939. * @sample {highcharts} highcharts/demo/venn-diagram/
  1940. * Venn diagram
  1941. * @sample {highcharts} highcharts/demo/euler-diagram/
  1942. * Euler diagram
  1943. *
  1944. * @type {number}
  1945. * @since 7.0.0
  1946. * @product highcharts
  1947. * @apioption series.venn.data.value
  1948. */
  1949. /**
  1950. * The set or sets the options will be applied to. If a single entry is defined,
  1951. * then it will create a new set. If more than one entry is defined, then it
  1952. * will define the overlap between the sets in the array.
  1953. *
  1954. * @sample {highcharts} highcharts/demo/venn-diagram/
  1955. * Venn diagram
  1956. * @sample {highcharts} highcharts/demo/euler-diagram/
  1957. * Euler diagram
  1958. *
  1959. * @type {Array<string>}
  1960. * @since 7.0.0
  1961. * @product highcharts
  1962. * @apioption series.venn.data.sets
  1963. */
  1964. /**
  1965. * @excluding halo
  1966. * @apioption series.venn.states.hover
  1967. */
  1968. /**
  1969. * @excluding halo
  1970. * @apioption series.venn.states.select
  1971. */
  1972. ''; // detach doclets above
  1973. /* eslint-disable no-invalid-this */
  1974. // Modify final series options.
  1975. addEvent(VennSeries, 'afterSetOptions', function (e) {
  1976. var options = e.options,
  1977. states = options.states;
  1978. if (this.is('venn')) {
  1979. // Explicitly disable all halo options.
  1980. Object.keys(states).forEach(function (state) {
  1981. states[state].halo = false;
  1982. });
  1983. }
  1984. });
  1985. return VennSeries;
  1986. });
  1987. _registerModule(_modules, 'masters/modules/venn.src.js', [], function () {
  1988. });
  1989. }));