highcharts-3d.src.js 168 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918
  1. /**
  2. * @license Highcharts JS v8.0.0 (2019-12-10)
  3. *
  4. * 3D features for Highcharts JS
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. (function (factory) {
  10. if (typeof module === 'object' && module.exports) {
  11. factory['default'] = factory;
  12. module.exports = factory;
  13. } else if (typeof define === 'function' && define.amd) {
  14. define('highcharts/highcharts-3d', ['highcharts'], function (Highcharts) {
  15. factory(Highcharts);
  16. factory.Highcharts = Highcharts;
  17. return factory;
  18. });
  19. } else {
  20. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  21. }
  22. }(function (Highcharts) {
  23. var _modules = Highcharts ? Highcharts._modules : {};
  24. function _registerModule(obj, path, args, fn) {
  25. if (!obj.hasOwnProperty(path)) {
  26. obj[path] = fn.apply(null, args);
  27. }
  28. }
  29. _registerModule(_modules, 'parts-3d/Math.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  30. /* *
  31. *
  32. * (c) 2010-2019 Torstein Honsi
  33. *
  34. * License: www.highcharts.com/license
  35. *
  36. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  37. *
  38. * */
  39. var pick = U.pick;
  40. // Mathematical Functionility
  41. var deg2rad = H.deg2rad;
  42. /* eslint-disable max-len */
  43. /**
  44. * Apply 3-D rotation
  45. * Euler Angles (XYZ):
  46. * cosA = cos(Alfa|Roll)
  47. * cosB = cos(Beta|Pitch)
  48. * cosG = cos(Gamma|Yaw)
  49. *
  50. * Composite rotation:
  51. * | cosB * cosG | cosB * sinG | -sinB |
  52. * | sinA * sinB * cosG - cosA * sinG | sinA * sinB * sinG + cosA * cosG | sinA * cosB |
  53. * | cosA * sinB * cosG + sinA * sinG | cosA * sinB * sinG - sinA * cosG | cosA * cosB |
  54. *
  55. * Now, Gamma/Yaw is not used (angle=0), so we assume cosG = 1 and sinG = 0, so
  56. * we get:
  57. * | cosB | 0 | - sinB |
  58. * | sinA * sinB | cosA | sinA * cosB |
  59. * | cosA * sinB | - sinA | cosA * cosB |
  60. *
  61. * But in browsers, y is reversed, so we get sinA => -sinA. The general result
  62. * is:
  63. * | cosB | 0 | - sinB | | x | | px |
  64. * | - sinA * sinB | cosA | - sinA * cosB | x | y | = | py |
  65. * | cosA * sinB | sinA | cosA * cosB | | z | | pz |
  66. *
  67. * @private
  68. * @function rotate3D
  69. */
  70. /* eslint-enable max-len */
  71. /**
  72. * @private
  73. * @param {number} x
  74. * X coordinate
  75. * @param {number} y
  76. * Y coordinate
  77. * @param {number} z
  78. * Z coordinate
  79. * @param {Highcharts.Rotation3dObject} angles
  80. * Rotation angles
  81. * @return {Highcharts.Rotation3dObject}
  82. * Rotated position
  83. */
  84. function rotate3D(x, y, z, angles) {
  85. return {
  86. x: angles.cosB * x - angles.sinB * z,
  87. y: -angles.sinA * angles.sinB * x + angles.cosA * y -
  88. angles.cosB * angles.sinA * z,
  89. z: angles.cosA * angles.sinB * x + angles.sinA * y +
  90. angles.cosA * angles.cosB * z
  91. };
  92. }
  93. /**
  94. * Perspective3D function is available in global Highcharts scope because is
  95. * needed also outside of perspective() function (#8042).
  96. * @private
  97. * @function Highcharts.perspective3D
  98. *
  99. * @param {Highcharts.Position3dObject} coordinate
  100. * 3D position
  101. *
  102. * @param {Highcharts.Position3dObject} origin
  103. * 3D root position
  104. *
  105. * @param {number} distance
  106. * Perspective distance
  107. *
  108. * @return {Highcharts.PositionObject}
  109. * Perspective 3D Position
  110. *
  111. * @requires highcharts-3d
  112. */
  113. H.perspective3D = function (coordinate, origin, distance) {
  114. var projection = ((distance > 0) && (distance < Number.POSITIVE_INFINITY)) ?
  115. distance / (coordinate.z + origin.z + distance) :
  116. 1;
  117. return {
  118. x: coordinate.x * projection,
  119. y: coordinate.y * projection
  120. };
  121. };
  122. /**
  123. * Transforms a given array of points according to the angles in chart.options.
  124. *
  125. * @private
  126. * @function Highcharts.perspective
  127. *
  128. * @param {Array<Highcharts.Position3dObject>} points
  129. * The array of points
  130. *
  131. * @param {Highcharts.Chart} chart
  132. * The chart
  133. *
  134. * @param {boolean} [insidePlotArea]
  135. * Wether to verifiy the points are inside the plotArea
  136. *
  137. * @return {Array<Highcharts.Position3dObject>}
  138. * An array of transformed points
  139. *
  140. * @requires highcharts-3d
  141. */
  142. H.perspective = function (points, chart, insidePlotArea) {
  143. var options3d = chart.options.chart.options3d, inverted = insidePlotArea ? chart.inverted : false, origin = {
  144. x: chart.plotWidth / 2,
  145. y: chart.plotHeight / 2,
  146. z: options3d.depth / 2,
  147. vd: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0)
  148. }, scale = chart.scale3d || 1, beta = deg2rad * options3d.beta * (inverted ? -1 : 1), alpha = deg2rad * options3d.alpha * (inverted ? -1 : 1), angles = {
  149. cosA: Math.cos(alpha),
  150. cosB: Math.cos(-beta),
  151. sinA: Math.sin(alpha),
  152. sinB: Math.sin(-beta)
  153. };
  154. if (!insidePlotArea) {
  155. origin.x += chart.plotLeft;
  156. origin.y += chart.plotTop;
  157. }
  158. // Transform each point
  159. return points.map(function (point) {
  160. var rotated = rotate3D((inverted ? point.y : point.x) - origin.x, (inverted ? point.x : point.y) - origin.y, (point.z || 0) - origin.z, angles),
  161. // Apply perspective
  162. coordinate = H.perspective3D(rotated, origin, origin.vd);
  163. // Apply translation
  164. coordinate.x = coordinate.x * scale + origin.x;
  165. coordinate.y = coordinate.y * scale + origin.y;
  166. coordinate.z = rotated.z * scale + origin.z;
  167. return {
  168. x: (inverted ? coordinate.y : coordinate.x),
  169. y: (inverted ? coordinate.x : coordinate.y),
  170. z: coordinate.z
  171. };
  172. });
  173. };
  174. /**
  175. * Calculate a distance from camera to points - made for calculating zIndex of
  176. * scatter points.
  177. *
  178. * @private
  179. * @function Highcharts.pointCameraDistance
  180. *
  181. * @param {Highcharts.Dictionary<number>} coordinates
  182. * Coordinates of the specific point
  183. *
  184. * @param {Highcharts.Chart} chart
  185. * Related chart
  186. *
  187. * @return {number}
  188. * Distance from camera to point
  189. *
  190. * @requires highcharts-3d
  191. */
  192. H.pointCameraDistance = function (coordinates, chart) {
  193. var options3d = chart.options.chart.options3d, cameraPosition = {
  194. x: chart.plotWidth / 2,
  195. y: chart.plotHeight / 2,
  196. z: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0) +
  197. options3d.depth
  198. }, distance = Math.sqrt(Math.pow(cameraPosition.x - coordinates.plotX, 2) +
  199. Math.pow(cameraPosition.y - coordinates.plotY, 2) +
  200. Math.pow(cameraPosition.z - coordinates.plotZ, 2));
  201. return distance;
  202. };
  203. /**
  204. * Calculate area of a 2D polygon using Shoelace algorithm
  205. * http://en.wikipedia.org/wiki/Shoelace_formula
  206. *
  207. * @private
  208. * @function Highcharts.shapeArea
  209. *
  210. * @param {Array<Highcharts.PositionObject>} vertexes
  211. * 2D Polygon
  212. *
  213. * @return {number}
  214. * Calculated area
  215. *
  216. * @requires highcharts-3d
  217. */
  218. H.shapeArea = function (vertexes) {
  219. var area = 0, i, j;
  220. for (i = 0; i < vertexes.length; i++) {
  221. j = (i + 1) % vertexes.length;
  222. area += vertexes[i].x * vertexes[j].y - vertexes[j].x * vertexes[i].y;
  223. }
  224. return area / 2;
  225. };
  226. /**
  227. * Calculate area of a 3D polygon after perspective projection
  228. *
  229. * @private
  230. * @function Highcharts.shapeArea3d
  231. *
  232. * @param {Array<Highcharts.Position3dObject>} vertexes
  233. * 3D Polygon
  234. *
  235. * @param {Highcharts.Chart} chart
  236. * Related chart
  237. *
  238. * @param {boolean} [insidePlotArea]
  239. * Wether to verifiy the points are inside the plotArea
  240. *
  241. * @return {number}
  242. * Calculated area
  243. *
  244. * @requires highcharts-3d
  245. */
  246. H.shapeArea3d = function (vertexes, chart, insidePlotArea) {
  247. return H.shapeArea(H.perspective(vertexes, chart, insidePlotArea));
  248. };
  249. });
  250. _registerModule(_modules, 'parts-3d/SVGRenderer.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  251. /* *
  252. *
  253. * (c) 2010-2019 Torstein Honsi
  254. *
  255. * Extensions to the SVGRenderer class to enable 3D shapes
  256. *
  257. * License: www.highcharts.com/license
  258. *
  259. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  260. *
  261. * */
  262. var animObject = U.animObject, defined = U.defined, extend = U.extend, objectEach = U.objectEach, pick = U.pick;
  263. var cos = Math.cos, PI = Math.PI, sin = Math.sin;
  264. var charts = H.charts, color = H.color, deg2rad = H.deg2rad, merge = H.merge, perspective = H.perspective, SVGElement = H.SVGElement, SVGRenderer = H.SVGRenderer,
  265. // internal:
  266. dFactor, element3dMethods, cuboidMethods;
  267. /*
  268. EXTENSION TO THE SVG-RENDERER TO ENABLE 3D SHAPES
  269. */
  270. // HELPER METHODS
  271. dFactor = (4 * (Math.sqrt(2) - 1) / 3) / (PI / 2);
  272. /* eslint-disable no-invalid-this, valid-jsdoc */
  273. /**
  274. * Method to construct a curved path. Can 'wrap' around more then 180 degrees.
  275. * @private
  276. */
  277. function curveTo(cx, cy, rx, ry, start, end, dx, dy) {
  278. var result = [], arcAngle = end - start;
  279. if ((end > start) && (end - start > Math.PI / 2 + 0.0001)) {
  280. result = result.concat(curveTo(cx, cy, rx, ry, start, start + (Math.PI / 2), dx, dy));
  281. result = result.concat(curveTo(cx, cy, rx, ry, start + (Math.PI / 2), end, dx, dy));
  282. return result;
  283. }
  284. if ((end < start) && (start - end > Math.PI / 2 + 0.0001)) {
  285. result = result.concat(curveTo(cx, cy, rx, ry, start, start - (Math.PI / 2), dx, dy));
  286. result = result.concat(curveTo(cx, cy, rx, ry, start - (Math.PI / 2), end, dx, dy));
  287. return result;
  288. }
  289. return [
  290. 'C',
  291. cx + (rx * Math.cos(start)) -
  292. ((rx * dFactor * arcAngle) * Math.sin(start)) + dx,
  293. cy + (ry * Math.sin(start)) +
  294. ((ry * dFactor * arcAngle) * Math.cos(start)) + dy,
  295. cx + (rx * Math.cos(end)) +
  296. ((rx * dFactor * arcAngle) * Math.sin(end)) + dx,
  297. cy + (ry * Math.sin(end)) -
  298. ((ry * dFactor * arcAngle) * Math.cos(end)) + dy,
  299. cx + (rx * Math.cos(end)) + dx,
  300. cy + (ry * Math.sin(end)) + dy
  301. ];
  302. }
  303. SVGRenderer.prototype.toLinePath = function (points, closed) {
  304. var result = [];
  305. // Put "L x y" for each point
  306. points.forEach(function (point) {
  307. result.push('L', point.x, point.y);
  308. });
  309. if (points.length) {
  310. // Set the first element to M
  311. result[0] = 'M';
  312. // If it is a closed line, add Z
  313. if (closed) {
  314. result.push('Z');
  315. }
  316. }
  317. return result;
  318. };
  319. SVGRenderer.prototype.toLineSegments = function (points) {
  320. var result = [], m = true;
  321. points.forEach(function (point) {
  322. result.push(m ? 'M' : 'L', point.x, point.y);
  323. m = !m;
  324. });
  325. return result;
  326. };
  327. // A 3-D Face is defined by it's 3D vertexes, and is only visible if it's
  328. // vertexes are counter-clockwise (Back-face culling). It is used as a
  329. // polyhedron Element
  330. SVGRenderer.prototype.face3d = function (args) {
  331. var renderer = this, ret = this.createElement('path');
  332. ret.vertexes = [];
  333. ret.insidePlotArea = false;
  334. ret.enabled = true;
  335. ret.attr = function (hash) {
  336. if (typeof hash === 'object' &&
  337. (defined(hash.enabled) ||
  338. defined(hash.vertexes) ||
  339. defined(hash.insidePlotArea))) {
  340. this.enabled = pick(hash.enabled, this.enabled);
  341. this.vertexes = pick(hash.vertexes, this.vertexes);
  342. this.insidePlotArea = pick(hash.insidePlotArea, this.insidePlotArea);
  343. delete hash.enabled;
  344. delete hash.vertexes;
  345. delete hash.insidePlotArea;
  346. var chart = charts[renderer.chartIndex], vertexes2d = perspective(this.vertexes, chart, this.insidePlotArea), path = renderer.toLinePath(vertexes2d, true), area = H.shapeArea(vertexes2d), visibility = (this.enabled && area > 0) ? 'visible' : 'hidden';
  347. hash.d = path;
  348. hash.visibility = visibility;
  349. }
  350. return SVGElement.prototype.attr.apply(this, arguments);
  351. };
  352. ret.animate = function (params) {
  353. if (typeof params === 'object' &&
  354. (defined(params.enabled) ||
  355. defined(params.vertexes) ||
  356. defined(params.insidePlotArea))) {
  357. this.enabled = pick(params.enabled, this.enabled);
  358. this.vertexes = pick(params.vertexes, this.vertexes);
  359. this.insidePlotArea = pick(params.insidePlotArea, this.insidePlotArea);
  360. delete params.enabled;
  361. delete params.vertexes;
  362. delete params.insidePlotArea;
  363. var chart = charts[renderer.chartIndex], vertexes2d = perspective(this.vertexes, chart, this.insidePlotArea), path = renderer.toLinePath(vertexes2d, true), area = H.shapeArea(vertexes2d), visibility = (this.enabled && area > 0) ? 'visible' : 'hidden';
  364. params.d = path;
  365. this.attr('visibility', visibility);
  366. }
  367. return SVGElement.prototype.animate.apply(this, arguments);
  368. };
  369. return ret.attr(args);
  370. };
  371. // A Polyhedron is a handy way of defining a group of 3-D faces. It's only
  372. // attribute is `faces`, an array of attributes of each one of it's Face3D
  373. // instances.
  374. SVGRenderer.prototype.polyhedron = function (args) {
  375. var renderer = this, result = this.g(), destroy = result.destroy;
  376. if (!this.styledMode) {
  377. result.attr({
  378. 'stroke-linejoin': 'round'
  379. });
  380. }
  381. result.faces = [];
  382. // destroy all children
  383. result.destroy = function () {
  384. for (var i = 0; i < result.faces.length; i++) {
  385. result.faces[i].destroy();
  386. }
  387. return destroy.call(this);
  388. };
  389. result.attr = function (hash, val, complete, continueAnimation) {
  390. if (typeof hash === 'object' && defined(hash.faces)) {
  391. while (result.faces.length > hash.faces.length) {
  392. result.faces.pop().destroy();
  393. }
  394. while (result.faces.length < hash.faces.length) {
  395. result.faces.push(renderer.face3d().add(result));
  396. }
  397. for (var i = 0; i < hash.faces.length; i++) {
  398. if (renderer.styledMode) {
  399. delete hash.faces[i].fill;
  400. }
  401. result.faces[i].attr(hash.faces[i], null, complete, continueAnimation);
  402. }
  403. delete hash.faces;
  404. }
  405. return SVGElement.prototype.attr.apply(this, arguments);
  406. };
  407. result.animate = function (params, duration, complete) {
  408. if (params && params.faces) {
  409. while (result.faces.length > params.faces.length) {
  410. result.faces.pop().destroy();
  411. }
  412. while (result.faces.length < params.faces.length) {
  413. result.faces.push(renderer.face3d().add(result));
  414. }
  415. for (var i = 0; i < params.faces.length; i++) {
  416. result.faces[i].animate(params.faces[i], duration, complete);
  417. }
  418. delete params.faces;
  419. }
  420. return SVGElement.prototype.animate.apply(this, arguments);
  421. };
  422. return result.attr(args);
  423. };
  424. // Base, abstract prototype member for 3D elements
  425. element3dMethods = {
  426. /**
  427. * The init is used by base - renderer.Element
  428. * @private
  429. */
  430. initArgs: function (args) {
  431. var elem3d = this, renderer = elem3d.renderer, paths = renderer[elem3d.pathType + 'Path'](args), zIndexes = paths.zIndexes;
  432. // build parts
  433. elem3d.parts.forEach(function (part) {
  434. elem3d[part] = renderer.path(paths[part]).attr({
  435. 'class': 'highcharts-3d-' + part,
  436. zIndex: zIndexes[part] || 0
  437. }).add(elem3d);
  438. });
  439. elem3d.attr({
  440. 'stroke-linejoin': 'round',
  441. zIndex: zIndexes.group
  442. });
  443. // store original destroy
  444. elem3d.originalDestroy = elem3d.destroy;
  445. elem3d.destroy = elem3d.destroyParts;
  446. },
  447. /**
  448. * Single property setter that applies options to each part
  449. * @private
  450. */
  451. singleSetterForParts: function (prop, val, values, verb, duration, complete) {
  452. var elem3d = this, newAttr = {}, optionsToApply = [null, null, (verb || 'attr'), duration, complete], hasZIndexes = values && values.zIndexes;
  453. if (!values) {
  454. newAttr[prop] = val;
  455. optionsToApply[0] = newAttr;
  456. }
  457. else {
  458. objectEach(values, function (partVal, part) {
  459. newAttr[part] = {};
  460. newAttr[part][prop] = partVal;
  461. // include zIndexes if provided
  462. if (hasZIndexes) {
  463. newAttr[part].zIndex = values.zIndexes[part] || 0;
  464. }
  465. });
  466. optionsToApply[1] = newAttr;
  467. }
  468. return elem3d.processParts.apply(elem3d, optionsToApply);
  469. },
  470. /**
  471. * Calls function for each part. Used for attr, animate and destroy.
  472. * @private
  473. */
  474. processParts: function (props, partsProps, verb, duration, complete) {
  475. var elem3d = this;
  476. elem3d.parts.forEach(function (part) {
  477. // if different props for different parts
  478. if (partsProps) {
  479. props = pick(partsProps[part], false);
  480. }
  481. // only if something to set, but allow undefined
  482. if (props !== false) {
  483. elem3d[part][verb](props, duration, complete);
  484. }
  485. });
  486. return elem3d;
  487. },
  488. /**
  489. * Destroy all parts
  490. * @private
  491. */
  492. destroyParts: function () {
  493. this.processParts(null, null, 'destroy');
  494. return this.originalDestroy();
  495. }
  496. };
  497. // CUBOID
  498. cuboidMethods = H.merge(element3dMethods, {
  499. parts: ['front', 'top', 'side'],
  500. pathType: 'cuboid',
  501. attr: function (args, val, complete, continueAnimation) {
  502. // Resolve setting attributes by string name
  503. if (typeof args === 'string' && typeof val !== 'undefined') {
  504. var key = args;
  505. args = {};
  506. args[key] = val;
  507. }
  508. if (args.shapeArgs || defined(args.x)) {
  509. return this.singleSetterForParts('d', null, this.renderer[this.pathType + 'Path'](args.shapeArgs || args));
  510. }
  511. return SVGElement.prototype.attr.call(this, args, void 0, complete, continueAnimation);
  512. },
  513. animate: function (args, duration, complete) {
  514. if (defined(args.x) && defined(args.y)) {
  515. var paths = this.renderer[this.pathType + 'Path'](args);
  516. this.singleSetterForParts('d', null, paths, 'animate', duration, complete);
  517. this.attr({
  518. zIndex: paths.zIndexes.group
  519. });
  520. }
  521. else {
  522. SVGElement.prototype.animate.call(this, args, duration, complete);
  523. }
  524. return this;
  525. },
  526. fillSetter: function (fill) {
  527. this.singleSetterForParts('fill', null, {
  528. front: fill,
  529. top: color(fill).brighten(0.1).get(),
  530. side: color(fill).brighten(-0.1).get()
  531. });
  532. // fill for animation getter (#6776)
  533. this.color = this.fill = fill;
  534. return this;
  535. }
  536. });
  537. // set them up
  538. SVGRenderer.prototype.elements3d = {
  539. base: element3dMethods,
  540. cuboid: cuboidMethods
  541. };
  542. /**
  543. * return result, generalization
  544. * @private
  545. * @requires highcharts-3d
  546. */
  547. SVGRenderer.prototype.element3d = function (type, shapeArgs) {
  548. // base
  549. var ret = this.g();
  550. // extend
  551. extend(ret, this.elements3d[type]);
  552. // init
  553. ret.initArgs(shapeArgs);
  554. // return
  555. return ret;
  556. };
  557. // generelized, so now use simply
  558. SVGRenderer.prototype.cuboid = function (shapeArgs) {
  559. return this.element3d('cuboid', shapeArgs);
  560. };
  561. // Generates a cuboid path and zIndexes
  562. H.SVGRenderer.prototype.cuboidPath = function (shapeArgs) {
  563. var x = shapeArgs.x, y = shapeArgs.y, z = shapeArgs.z, h = shapeArgs.height, w = shapeArgs.width, d = shapeArgs.depth, chart = charts[this.chartIndex], front, back, top, bottom, left, right, shape, path1, path2, path3, isFront, isTop, isRight, options3d = chart.options.chart.options3d, alpha = options3d.alpha,
  564. // Priority for x axis is the biggest,
  565. // because of x direction has biggest influence on zIndex
  566. incrementX = 10000,
  567. // y axis has the smallest priority in case of our charts
  568. // (needs to be set because of stacking)
  569. incrementY = 10, incrementZ = 100, zIndex = 0,
  570. // The 8 corners of the cube
  571. pArr = [{
  572. x: x,
  573. y: y,
  574. z: z
  575. }, {
  576. x: x + w,
  577. y: y,
  578. z: z
  579. }, {
  580. x: x + w,
  581. y: y + h,
  582. z: z
  583. }, {
  584. x: x,
  585. y: y + h,
  586. z: z
  587. }, {
  588. x: x,
  589. y: y + h,
  590. z: z + d
  591. }, {
  592. x: x + w,
  593. y: y + h,
  594. z: z + d
  595. }, {
  596. x: x + w,
  597. y: y,
  598. z: z + d
  599. }, {
  600. x: x,
  601. y: y,
  602. z: z + d
  603. }], pickShape;
  604. // apply perspective
  605. pArr = perspective(pArr, chart, shapeArgs.insidePlotArea);
  606. /**
  607. * helper method to decide which side is visible
  608. * @private
  609. */
  610. function mapPath(i) {
  611. return pArr[i];
  612. }
  613. /**
  614. * First value - path with specific side
  615. * Second value - added information about side for later calculations.
  616. * Possible second values are 0 for path1, 1 for path2 and -1 for no path
  617. * chosen.
  618. * @private
  619. */
  620. pickShape = function (path1, path2) {
  621. var ret = [[], -1];
  622. path1 = path1.map(mapPath);
  623. path2 = path2.map(mapPath);
  624. if (H.shapeArea(path1) < 0) {
  625. ret = [path1, 0];
  626. }
  627. else if (H.shapeArea(path2) < 0) {
  628. ret = [path2, 1];
  629. }
  630. return ret;
  631. };
  632. // front or back
  633. front = [3, 2, 1, 0];
  634. back = [7, 6, 5, 4];
  635. shape = pickShape(front, back);
  636. path1 = shape[0];
  637. isFront = shape[1];
  638. // top or bottom
  639. top = [1, 6, 7, 0];
  640. bottom = [4, 5, 2, 3];
  641. shape = pickShape(top, bottom);
  642. path2 = shape[0];
  643. isTop = shape[1];
  644. // side
  645. right = [1, 2, 5, 6];
  646. left = [0, 7, 4, 3];
  647. shape = pickShape(right, left);
  648. path3 = shape[0];
  649. isRight = shape[1];
  650. /* New block used for calculating zIndex. It is basing on X, Y and Z
  651. position of specific columns. All zIndexes (for X, Y and Z values) are
  652. added to the final zIndex, where every value has different priority. The
  653. biggest priority is in X and Z directions, the lowest index is for
  654. stacked columns (Y direction and the same X and Z positions). Big
  655. differences between priorities is made because we need to ensure that
  656. even for big changes in Y and Z parameters all columns will be drawn
  657. correctly. */
  658. if (isRight === 1) {
  659. zIndex += incrementX * (1000 - x);
  660. }
  661. else if (!isRight) {
  662. zIndex += incrementX * x;
  663. }
  664. zIndex += incrementY * (!isTop ||
  665. // Numbers checked empirically
  666. (alpha >= 0 && alpha <= 180 || alpha < 360 && alpha > 357.5) ?
  667. chart.plotHeight - y : 10 + y);
  668. if (isFront === 1) {
  669. zIndex += incrementZ * (z);
  670. }
  671. else if (!isFront) {
  672. zIndex += incrementZ * (1000 - z);
  673. }
  674. return {
  675. front: this.toLinePath(path1, true),
  676. top: this.toLinePath(path2, true),
  677. side: this.toLinePath(path3, true),
  678. zIndexes: {
  679. group: Math.round(zIndex)
  680. },
  681. // additional info about zIndexes
  682. isFront: isFront,
  683. isTop: isTop
  684. }; // #4774
  685. };
  686. // SECTORS //
  687. H.SVGRenderer.prototype.arc3d = function (attribs) {
  688. var wrapper = this.g(), renderer = wrapper.renderer, customAttribs = ['x', 'y', 'r', 'innerR', 'start', 'end'];
  689. /**
  690. * Get custom attributes. Don't mutate the original object and return an
  691. * object with only custom attr.
  692. * @private
  693. */
  694. function suckOutCustom(params) {
  695. var hasCA = false, ca = {}, key;
  696. params = merge(params); // Don't mutate the original object
  697. for (key in params) {
  698. if (customAttribs.indexOf(key) !== -1) {
  699. ca[key] = params[key];
  700. delete params[key];
  701. hasCA = true;
  702. }
  703. }
  704. return hasCA ? ca : false;
  705. }
  706. attribs = merge(attribs);
  707. attribs.alpha = (attribs.alpha || 0) * deg2rad;
  708. attribs.beta = (attribs.beta || 0) * deg2rad;
  709. // Create the different sub sections of the shape
  710. wrapper.top = renderer.path();
  711. wrapper.side1 = renderer.path();
  712. wrapper.side2 = renderer.path();
  713. wrapper.inn = renderer.path();
  714. wrapper.out = renderer.path();
  715. // Add all faces
  716. wrapper.onAdd = function () {
  717. var parent = wrapper.parentGroup, className = wrapper.attr('class');
  718. wrapper.top.add(wrapper);
  719. // These faces are added outside the wrapper group because the z index
  720. // relates to neighbour elements as well
  721. ['out', 'inn', 'side1', 'side2'].forEach(function (face) {
  722. wrapper[face]
  723. .attr({
  724. 'class': className + ' highcharts-3d-side'
  725. })
  726. .add(parent);
  727. });
  728. };
  729. // Cascade to faces
  730. ['addClass', 'removeClass'].forEach(function (fn) {
  731. wrapper[fn] = function () {
  732. var args = arguments;
  733. ['top', 'out', 'inn', 'side1', 'side2'].forEach(function (face) {
  734. wrapper[face][fn].apply(wrapper[face], args);
  735. });
  736. };
  737. });
  738. /**
  739. * Compute the transformed paths and set them to the composite shapes
  740. * @private
  741. */
  742. wrapper.setPaths = function (attribs) {
  743. var paths = wrapper.renderer.arc3dPath(attribs), zIndex = paths.zTop * 100;
  744. wrapper.attribs = attribs;
  745. wrapper.top.attr({ d: paths.top, zIndex: paths.zTop });
  746. wrapper.inn.attr({ d: paths.inn, zIndex: paths.zInn });
  747. wrapper.out.attr({ d: paths.out, zIndex: paths.zOut });
  748. wrapper.side1.attr({ d: paths.side1, zIndex: paths.zSide1 });
  749. wrapper.side2.attr({ d: paths.side2, zIndex: paths.zSide2 });
  750. // show all children
  751. wrapper.zIndex = zIndex;
  752. wrapper.attr({ zIndex: zIndex });
  753. // Set the radial gradient center the first time
  754. if (attribs.center) {
  755. wrapper.top.setRadialReference(attribs.center);
  756. delete attribs.center;
  757. }
  758. };
  759. wrapper.setPaths(attribs);
  760. /**
  761. * Apply the fill to the top and a darker shade to the sides
  762. * @private
  763. */
  764. wrapper.fillSetter = function (value) {
  765. var darker = color(value).brighten(-0.1).get();
  766. this.fill = value;
  767. this.side1.attr({ fill: darker });
  768. this.side2.attr({ fill: darker });
  769. this.inn.attr({ fill: darker });
  770. this.out.attr({ fill: darker });
  771. this.top.attr({ fill: value });
  772. return this;
  773. };
  774. // Apply the same value to all. These properties cascade down to the
  775. // children when set to the composite arc3d.
  776. ['opacity', 'translateX', 'translateY', 'visibility'].forEach(function (setter) {
  777. wrapper[setter + 'Setter'] = function (value, key) {
  778. wrapper[key] = value;
  779. ['out', 'inn', 'side1', 'side2', 'top'].forEach(function (el) {
  780. wrapper[el].attr(key, value);
  781. });
  782. };
  783. });
  784. // Override attr to remove shape attributes and use those to set child paths
  785. wrapper.attr = function (params) {
  786. var ca;
  787. if (typeof params === 'object') {
  788. ca = suckOutCustom(params);
  789. if (ca) {
  790. extend(wrapper.attribs, ca);
  791. wrapper.setPaths(wrapper.attribs);
  792. }
  793. }
  794. return SVGElement.prototype.attr.apply(wrapper, arguments);
  795. };
  796. // Override the animate function by sucking out custom parameters related to
  797. // the shapes directly, and update the shapes from the animation step.
  798. wrapper.animate = function (params, animation, complete) {
  799. var ca, from = this.attribs, to, anim, randomProp = 'data-' + Math.random().toString(26).substring(2, 9);
  800. // Attribute-line properties connected to 3D. These shouldn't have been
  801. // in the attribs collection in the first place.
  802. delete params.center;
  803. delete params.z;
  804. delete params.depth;
  805. delete params.alpha;
  806. delete params.beta;
  807. anim = animObject(pick(animation, this.renderer.globalAnimation));
  808. if (anim.duration) {
  809. ca = suckOutCustom(params);
  810. // Params need to have a property in order for the step to run
  811. // (#5765, #7097, #7437)
  812. wrapper[randomProp] = 0;
  813. params[randomProp] = 1;
  814. wrapper[randomProp + 'Setter'] = H.noop;
  815. if (ca) {
  816. to = ca;
  817. anim.step = function (a, fx) {
  818. /**
  819. * @private
  820. */
  821. function interpolate(key) {
  822. return from[key] + (pick(to[key], from[key]) -
  823. from[key]) * fx.pos;
  824. }
  825. if (fx.prop === randomProp) {
  826. fx.elem.setPaths(merge(from, {
  827. x: interpolate('x'),
  828. y: interpolate('y'),
  829. r: interpolate('r'),
  830. innerR: interpolate('innerR'),
  831. start: interpolate('start'),
  832. end: interpolate('end')
  833. }));
  834. }
  835. };
  836. }
  837. animation = anim; // Only when duration (#5572)
  838. }
  839. return SVGElement.prototype.animate.call(this, params, animation, complete);
  840. };
  841. // destroy all children
  842. wrapper.destroy = function () {
  843. this.top.destroy();
  844. this.out.destroy();
  845. this.inn.destroy();
  846. this.side1.destroy();
  847. this.side2.destroy();
  848. return SVGElement.prototype.destroy.call(this);
  849. };
  850. // hide all children
  851. wrapper.hide = function () {
  852. this.top.hide();
  853. this.out.hide();
  854. this.inn.hide();
  855. this.side1.hide();
  856. this.side2.hide();
  857. };
  858. wrapper.show = function (inherit) {
  859. this.top.show(inherit);
  860. this.out.show(inherit);
  861. this.inn.show(inherit);
  862. this.side1.show(inherit);
  863. this.side2.show(inherit);
  864. };
  865. return wrapper;
  866. };
  867. // Generate the paths required to draw a 3D arc
  868. SVGRenderer.prototype.arc3dPath = function (shapeArgs) {
  869. var cx = shapeArgs.x, // x coordinate of the center
  870. cy = shapeArgs.y, // y coordinate of the center
  871. start = shapeArgs.start, // start angle
  872. end = shapeArgs.end - 0.00001, // end angle
  873. r = shapeArgs.r, // radius
  874. ir = shapeArgs.innerR || 0, // inner radius
  875. d = shapeArgs.depth || 0, // depth
  876. alpha = shapeArgs.alpha, // alpha rotation of the chart
  877. beta = shapeArgs.beta; // beta rotation of the chart
  878. // Derived Variables
  879. var cs = Math.cos(start), // cosinus of the start angle
  880. ss = Math.sin(start), // sinus of the start angle
  881. ce = Math.cos(end), // cosinus of the end angle
  882. se = Math.sin(end), // sinus of the end angle
  883. rx = r * Math.cos(beta), // x-radius
  884. ry = r * Math.cos(alpha), // y-radius
  885. irx = ir * Math.cos(beta), // x-radius (inner)
  886. iry = ir * Math.cos(alpha), // y-radius (inner)
  887. dx = d * Math.sin(beta), // distance between top and bottom in x
  888. dy = d * Math.sin(alpha); // distance between top and bottom in y
  889. // TOP
  890. var top = ['M', cx + (rx * cs), cy + (ry * ss)];
  891. top = top.concat(curveTo(cx, cy, rx, ry, start, end, 0, 0));
  892. top = top.concat([
  893. 'L', cx + (irx * ce), cy + (iry * se)
  894. ]);
  895. top = top.concat(curveTo(cx, cy, irx, iry, end, start, 0, 0));
  896. top = top.concat(['Z']);
  897. // OUTSIDE
  898. var b = (beta > 0 ? Math.PI / 2 : 0), a = (alpha > 0 ? 0 : Math.PI / 2);
  899. var start2 = start > -b ? start : (end > -b ? -b : start), end2 = end < PI - a ? end : (start < PI - a ? PI - a : end), midEnd = 2 * PI - a;
  900. // When slice goes over bottom middle, need to add both, left and right
  901. // outer side. Additionally, when we cross right hand edge, create sharp
  902. // edge. Outer shape/wall:
  903. //
  904. // -------
  905. // / ^ \
  906. // 4) / / \ \ 1)
  907. // / / \ \
  908. // / / \ \
  909. // (c)=> ==== ==== <=(d)
  910. // \ \ / /
  911. // \ \<=(a)/ /
  912. // \ \ / / <=(b)
  913. // 3) \ v / 2)
  914. // -------
  915. //
  916. // (a) - inner side
  917. // (b) - outer side
  918. // (c) - left edge (sharp)
  919. // (d) - right edge (sharp)
  920. // 1..n - rendering order for startAngle = 0, when set to e.g 90, order
  921. // changes clockwise (1->2, 2->3, n->1) and counterclockwise for negative
  922. // startAngle
  923. var out = ['M', cx + (rx * cos(start2)), cy + (ry * sin(start2))];
  924. out = out.concat(curveTo(cx, cy, rx, ry, start2, end2, 0, 0));
  925. // When shape is wide, it can cross both, (c) and (d) edges, when using
  926. // startAngle
  927. if (end > midEnd && start < midEnd) {
  928. // Go to outer side
  929. out = out.concat([
  930. 'L', cx + (rx * cos(end2)) + dx, cy + (ry * sin(end2)) + dy
  931. ]);
  932. // Curve to the right edge of the slice (d)
  933. out = out.concat(curveTo(cx, cy, rx, ry, end2, midEnd, dx, dy));
  934. // Go to the inner side
  935. out = out.concat([
  936. 'L', cx + (rx * cos(midEnd)), cy + (ry * sin(midEnd))
  937. ]);
  938. // Curve to the true end of the slice
  939. out = out.concat(curveTo(cx, cy, rx, ry, midEnd, end, 0, 0));
  940. // Go to the outer side
  941. out = out.concat([
  942. 'L', cx + (rx * cos(end)) + dx, cy + (ry * sin(end)) + dy
  943. ]);
  944. // Go back to middle (d)
  945. out = out.concat(curveTo(cx, cy, rx, ry, end, midEnd, dx, dy));
  946. out = out.concat([
  947. 'L', cx + (rx * cos(midEnd)), cy + (ry * sin(midEnd))
  948. ]);
  949. // Go back to the left edge
  950. out = out.concat(curveTo(cx, cy, rx, ry, midEnd, end2, 0, 0));
  951. // But shape can cross also only (c) edge:
  952. }
  953. else if (end > PI - a && start < PI - a) {
  954. // Go to outer side
  955. out = out.concat([
  956. 'L',
  957. cx + (rx * Math.cos(end2)) + dx,
  958. cy + (ry * Math.sin(end2)) + dy
  959. ]);
  960. // Curve to the true end of the slice
  961. out = out.concat(curveTo(cx, cy, rx, ry, end2, end, dx, dy));
  962. // Go to the inner side
  963. out = out.concat([
  964. 'L', cx + (rx * Math.cos(end)), cy + (ry * Math.sin(end))
  965. ]);
  966. // Go back to the artifical end2
  967. out = out.concat(curveTo(cx, cy, rx, ry, end, end2, 0, 0));
  968. }
  969. out = out.concat([
  970. 'L', cx + (rx * Math.cos(end2)) + dx, cy + (ry * Math.sin(end2)) + dy
  971. ]);
  972. out = out.concat(curveTo(cx, cy, rx, ry, end2, start2, dx, dy));
  973. out = out.concat(['Z']);
  974. // INSIDE
  975. var inn = ['M', cx + (irx * cs), cy + (iry * ss)];
  976. inn = inn.concat(curveTo(cx, cy, irx, iry, start, end, 0, 0));
  977. inn = inn.concat([
  978. 'L', cx + (irx * Math.cos(end)) + dx, cy + (iry * Math.sin(end)) + dy
  979. ]);
  980. inn = inn.concat(curveTo(cx, cy, irx, iry, end, start, dx, dy));
  981. inn = inn.concat(['Z']);
  982. // SIDES
  983. var side1 = [
  984. 'M', cx + (rx * cs), cy + (ry * ss),
  985. 'L', cx + (rx * cs) + dx, cy + (ry * ss) + dy,
  986. 'L', cx + (irx * cs) + dx, cy + (iry * ss) + dy,
  987. 'L', cx + (irx * cs), cy + (iry * ss),
  988. 'Z'
  989. ];
  990. var side2 = [
  991. 'M', cx + (rx * ce), cy + (ry * se),
  992. 'L', cx + (rx * ce) + dx, cy + (ry * se) + dy,
  993. 'L', cx + (irx * ce) + dx, cy + (iry * se) + dy,
  994. 'L', cx + (irx * ce), cy + (iry * se),
  995. 'Z'
  996. ];
  997. // correction for changed position of vanishing point caused by alpha and
  998. // beta rotations
  999. var angleCorr = Math.atan2(dy, -dx), angleEnd = Math.abs(end + angleCorr), angleStart = Math.abs(start + angleCorr), angleMid = Math.abs((start + end) / 2 + angleCorr);
  1000. /**
  1001. * set to 0-PI range
  1002. * @private
  1003. */
  1004. function toZeroPIRange(angle) {
  1005. angle = angle % (2 * Math.PI);
  1006. if (angle > Math.PI) {
  1007. angle = 2 * Math.PI - angle;
  1008. }
  1009. return angle;
  1010. }
  1011. angleEnd = toZeroPIRange(angleEnd);
  1012. angleStart = toZeroPIRange(angleStart);
  1013. angleMid = toZeroPIRange(angleMid);
  1014. // *1e5 is to compensate pInt in zIndexSetter
  1015. var incPrecision = 1e5, a1 = angleMid * incPrecision, a2 = angleStart * incPrecision, a3 = angleEnd * incPrecision;
  1016. return {
  1017. top: top,
  1018. // max angle is PI, so this is always higher
  1019. zTop: Math.PI * incPrecision + 1,
  1020. out: out,
  1021. zOut: Math.max(a1, a2, a3),
  1022. inn: inn,
  1023. zInn: Math.max(a1, a2, a3),
  1024. side1: side1,
  1025. zSide1: a3 * 0.99,
  1026. side2: side2,
  1027. zSide2: a2 * 0.99
  1028. };
  1029. };
  1030. });
  1031. _registerModule(_modules, 'parts-3d/Chart.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  1032. /* *
  1033. *
  1034. * (c) 2010-2019 Torstein Honsi
  1035. *
  1036. * Extension for 3D charts
  1037. *
  1038. * License: www.highcharts.com/license
  1039. *
  1040. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  1041. *
  1042. * */
  1043. var isArray = U.isArray, pick = U.pick, wrap = U.wrap;
  1044. var addEvent = H.addEvent, Chart = H.Chart, merge = H.merge, perspective = H.perspective;
  1045. /**
  1046. * Shorthand to check the is3d flag.
  1047. * @private
  1048. * @return {boolean}
  1049. * Whether it is a 3D chart.
  1050. */
  1051. Chart.prototype.is3d = function () {
  1052. return (this.options.chart.options3d &&
  1053. this.options.chart.options3d.enabled); // #4280
  1054. };
  1055. Chart.prototype.propsRequireDirtyBox.push('chart.options3d');
  1056. Chart.prototype.propsRequireUpdateSeries.push('chart.options3d');
  1057. /* eslint-disable no-invalid-this */
  1058. // Legacy support for HC < 6 to make 'scatter' series in a 3D chart route to the
  1059. // real 'scatter3d' series type.
  1060. addEvent(Chart, 'afterInit', function () {
  1061. var options = this.options;
  1062. if (this.is3d()) {
  1063. (options.series || []).forEach(function (s) {
  1064. var type = s.type ||
  1065. options.chart.type ||
  1066. options.chart.defaultSeriesType;
  1067. if (type === 'scatter') {
  1068. s.type = 'scatter3d';
  1069. }
  1070. });
  1071. }
  1072. });
  1073. // And do it on dynamic add (#8407)
  1074. addEvent(Chart, 'addSeries', function (e) {
  1075. if (this.is3d()) {
  1076. if (e.options.type === 'scatter') {
  1077. e.options.type = 'scatter3d';
  1078. }
  1079. }
  1080. });
  1081. /**
  1082. * Calculate scale of the 3D view. That is required to
  1083. * fit chart's 3D projection into the actual plotting area. Reported as #4933.
  1084. * @notice This function should ideally take the plot values instead of a chart
  1085. * object, but since the chart object is needed for perspective it is
  1086. * not practical. Possible to make both getScale and perspective more
  1087. * logical and also immutable.
  1088. *
  1089. * @private
  1090. * @function getScale
  1091. *
  1092. * @param {Highcharts.Chart} chart
  1093. * Chart object
  1094. *
  1095. * @param {number} depth
  1096. * The depth of the chart
  1097. *
  1098. * @return {number}
  1099. * The scale to fit the 3D chart into the plotting area.
  1100. *
  1101. * @requires highcharts-3d
  1102. */
  1103. function getScale(chart, depth) {
  1104. var plotLeft = chart.plotLeft, plotRight = chart.plotWidth + plotLeft, plotTop = chart.plotTop, plotBottom = chart.plotHeight + plotTop, originX = plotLeft + chart.plotWidth / 2, originY = plotTop + chart.plotHeight / 2, bbox3d = {
  1105. minX: Number.MAX_VALUE,
  1106. maxX: -Number.MAX_VALUE,
  1107. minY: Number.MAX_VALUE,
  1108. maxY: -Number.MAX_VALUE
  1109. }, corners, scale = 1;
  1110. // Top left corners:
  1111. corners = [{
  1112. x: plotLeft,
  1113. y: plotTop,
  1114. z: 0
  1115. }, {
  1116. x: plotLeft,
  1117. y: plotTop,
  1118. z: depth
  1119. }];
  1120. // Top right corners:
  1121. [0, 1].forEach(function (i) {
  1122. corners.push({
  1123. x: plotRight,
  1124. y: corners[i].y,
  1125. z: corners[i].z
  1126. });
  1127. });
  1128. // All bottom corners:
  1129. [0, 1, 2, 3].forEach(function (i) {
  1130. corners.push({
  1131. x: corners[i].x,
  1132. y: plotBottom,
  1133. z: corners[i].z
  1134. });
  1135. });
  1136. // Calculate 3D corners:
  1137. corners = perspective(corners, chart, false);
  1138. // Get bounding box of 3D element:
  1139. corners.forEach(function (corner) {
  1140. bbox3d.minX = Math.min(bbox3d.minX, corner.x);
  1141. bbox3d.maxX = Math.max(bbox3d.maxX, corner.x);
  1142. bbox3d.minY = Math.min(bbox3d.minY, corner.y);
  1143. bbox3d.maxY = Math.max(bbox3d.maxY, corner.y);
  1144. });
  1145. // Left edge:
  1146. if (plotLeft > bbox3d.minX) {
  1147. scale = Math.min(scale, 1 - Math.abs((plotLeft + originX) / (bbox3d.minX + originX)) % 1);
  1148. }
  1149. // Right edge:
  1150. if (plotRight < bbox3d.maxX) {
  1151. scale = Math.min(scale, (plotRight - originX) / (bbox3d.maxX - originX));
  1152. }
  1153. // Top edge:
  1154. if (plotTop > bbox3d.minY) {
  1155. if (bbox3d.minY < 0) {
  1156. scale = Math.min(scale, (plotTop + originY) / (-bbox3d.minY + plotTop + originY));
  1157. }
  1158. else {
  1159. scale = Math.min(scale, 1 - (plotTop + originY) / (bbox3d.minY + originY) % 1);
  1160. }
  1161. }
  1162. // Bottom edge:
  1163. if (plotBottom < bbox3d.maxY) {
  1164. scale = Math.min(scale, Math.abs((plotBottom - originY) / (bbox3d.maxY - originY)));
  1165. }
  1166. return scale;
  1167. }
  1168. wrap(H.Chart.prototype, 'isInsidePlot', function (proceed) {
  1169. return this.is3d() || proceed.apply(this, [].slice.call(arguments, 1));
  1170. });
  1171. var defaultOptions = H.getOptions();
  1172. /**
  1173. * @optionparent
  1174. */
  1175. var extendedOptions = {
  1176. chart: {
  1177. /**
  1178. * Options to render charts in 3 dimensions. This feature requires
  1179. * `highcharts-3d.js`, found in the download package or online at
  1180. * [code.highcharts.com/highcharts-3d.js](http://code.highcharts.com/highcharts-3d.js).
  1181. *
  1182. * @since 4.0
  1183. * @product highcharts
  1184. * @requires highcharts-3d
  1185. */
  1186. options3d: {
  1187. /**
  1188. * Wether to render the chart using the 3D functionality.
  1189. *
  1190. * @since 4.0
  1191. * @product highcharts
  1192. */
  1193. enabled: false,
  1194. /**
  1195. * One of the two rotation angles for the chart.
  1196. *
  1197. * @since 4.0
  1198. * @product highcharts
  1199. */
  1200. alpha: 0,
  1201. /**
  1202. * One of the two rotation angles for the chart.
  1203. *
  1204. * @since 4.0
  1205. * @product highcharts
  1206. */
  1207. beta: 0,
  1208. /**
  1209. * The total depth of the chart.
  1210. *
  1211. * @since 4.0
  1212. * @product highcharts
  1213. */
  1214. depth: 100,
  1215. /**
  1216. * Whether the 3d box should automatically adjust to the chart plot
  1217. * area.
  1218. *
  1219. * @since 4.2.4
  1220. * @product highcharts
  1221. */
  1222. fitToPlot: true,
  1223. /**
  1224. * Defines the distance the viewer is standing in front of the
  1225. * chart, this setting is important to calculate the perspective
  1226. * effect in column and scatter charts. It is not used for 3D pie
  1227. * charts.
  1228. *
  1229. * @since 4.0
  1230. * @product highcharts
  1231. */
  1232. viewDistance: 25,
  1233. /**
  1234. * Set it to `"auto"` to automatically move the labels to the best
  1235. * edge.
  1236. *
  1237. * @type {"auto"|null}
  1238. * @since 5.0.12
  1239. * @product highcharts
  1240. */
  1241. axisLabelPosition: null,
  1242. /**
  1243. * Provides the option to draw a frame around the charts by defining
  1244. * a bottom, front and back panel.
  1245. *
  1246. * @since 4.0
  1247. * @product highcharts
  1248. * @requires highcharts-3d
  1249. */
  1250. frame: {
  1251. /**
  1252. * Whether the frames are visible.
  1253. */
  1254. visible: 'default',
  1255. /**
  1256. * General pixel thickness for the frame faces.
  1257. */
  1258. size: 1,
  1259. /**
  1260. * The bottom of the frame around a 3D chart.
  1261. *
  1262. * @since 4.0
  1263. * @product highcharts
  1264. * @requires highcharts-3d
  1265. */
  1266. /**
  1267. * The color of the panel.
  1268. *
  1269. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  1270. * @default transparent
  1271. * @since 4.0
  1272. * @product highcharts
  1273. * @apioption chart.options3d.frame.bottom.color
  1274. */
  1275. /**
  1276. * The thickness of the panel.
  1277. *
  1278. * @type {number}
  1279. * @default 1
  1280. * @since 4.0
  1281. * @product highcharts
  1282. * @apioption chart.options3d.frame.bottom.size
  1283. */
  1284. /**
  1285. * Whether to display the frame. Possible values are `true`,
  1286. * `false`, `"auto"` to display only the frames behind the data,
  1287. * and `"default"` to display faces behind the data based on the
  1288. * axis layout, ignoring the point of view.
  1289. *
  1290. * @sample {highcharts} highcharts/3d/scatter-frame/
  1291. * Auto frames
  1292. *
  1293. * @type {boolean|"default"|"auto"}
  1294. * @default default
  1295. * @since 5.0.12
  1296. * @product highcharts
  1297. * @apioption chart.options3d.frame.bottom.visible
  1298. */
  1299. /**
  1300. * The bottom of the frame around a 3D chart.
  1301. */
  1302. bottom: {},
  1303. /**
  1304. * The top of the frame around a 3D chart.
  1305. *
  1306. * @extends chart.options3d.frame.bottom
  1307. */
  1308. top: {},
  1309. /**
  1310. * The left side of the frame around a 3D chart.
  1311. *
  1312. * @extends chart.options3d.frame.bottom
  1313. */
  1314. left: {},
  1315. /**
  1316. * The right of the frame around a 3D chart.
  1317. *
  1318. * @extends chart.options3d.frame.bottom
  1319. */
  1320. right: {},
  1321. /**
  1322. * The back side of the frame around a 3D chart.
  1323. *
  1324. * @extends chart.options3d.frame.bottom
  1325. */
  1326. back: {},
  1327. /**
  1328. * The front of the frame around a 3D chart.
  1329. *
  1330. * @extends chart.options3d.frame.bottom
  1331. */
  1332. front: {}
  1333. }
  1334. }
  1335. }
  1336. };
  1337. merge(true, defaultOptions, extendedOptions);
  1338. // Add the required CSS classes for column sides (#6018)
  1339. addEvent(Chart, 'afterGetContainer', function () {
  1340. if (this.styledMode) {
  1341. this.renderer.definition({
  1342. tagName: 'style',
  1343. textContent: '.highcharts-3d-top{' +
  1344. 'filter: url(#highcharts-brighter)' +
  1345. '}\n' +
  1346. '.highcharts-3d-side{' +
  1347. 'filter: url(#highcharts-darker)' +
  1348. '}\n'
  1349. });
  1350. // Add add definitions used by brighter and darker faces of the cuboids.
  1351. [{
  1352. name: 'darker',
  1353. slope: 0.6
  1354. }, {
  1355. name: 'brighter',
  1356. slope: 1.4
  1357. }].forEach(function (cfg) {
  1358. this.renderer.definition({
  1359. tagName: 'filter',
  1360. id: 'highcharts-' + cfg.name,
  1361. children: [{
  1362. tagName: 'feComponentTransfer',
  1363. children: [{
  1364. tagName: 'feFuncR',
  1365. type: 'linear',
  1366. slope: cfg.slope
  1367. }, {
  1368. tagName: 'feFuncG',
  1369. type: 'linear',
  1370. slope: cfg.slope
  1371. }, {
  1372. tagName: 'feFuncB',
  1373. type: 'linear',
  1374. slope: cfg.slope
  1375. }]
  1376. }]
  1377. });
  1378. }, this);
  1379. }
  1380. });
  1381. wrap(Chart.prototype, 'setClassName', function (proceed) {
  1382. proceed.apply(this, [].slice.call(arguments, 1));
  1383. if (this.is3d()) {
  1384. this.container.className += ' highcharts-3d-chart';
  1385. }
  1386. });
  1387. addEvent(H.Chart, 'afterSetChartSize', function () {
  1388. var chart = this, options3d = chart.options.chart.options3d;
  1389. if (chart.is3d()) {
  1390. var inverted = chart.inverted, clipBox = chart.clipBox, margin = chart.margin, x = inverted ? 'y' : 'x', y = inverted ? 'x' : 'y', w = inverted ? 'height' : 'width', h = inverted ? 'width' : 'height';
  1391. clipBox[x] = -(margin[3] || 0);
  1392. clipBox[y] = -(margin[0] || 0);
  1393. clipBox[w] =
  1394. chart.chartWidth + (margin[3] || 0) + (margin[1] || 0);
  1395. clipBox[h] =
  1396. chart.chartHeight + (margin[0] || 0) + (margin[2] || 0);
  1397. // Set scale, used later in perspective method():
  1398. // getScale uses perspective, so scale3d has to be reset.
  1399. chart.scale3d = 1;
  1400. if (options3d.fitToPlot === true) {
  1401. chart.scale3d = getScale(chart, options3d.depth);
  1402. }
  1403. // Recalculate the 3d frame with every call of setChartSize,
  1404. // instead of doing it after every redraw(). It avoids ticks
  1405. // and axis title outside of chart.
  1406. chart.frame3d = this.get3dFrame(); // #7942
  1407. }
  1408. });
  1409. addEvent(Chart, 'beforeRedraw', function () {
  1410. if (this.is3d()) {
  1411. // Set to force a redraw of all elements
  1412. this.isDirtyBox = true;
  1413. }
  1414. });
  1415. addEvent(Chart, 'beforeRender', function () {
  1416. if (this.is3d()) {
  1417. this.frame3d = this.get3dFrame();
  1418. }
  1419. });
  1420. // Draw the series in the reverse order (#3803, #3917)
  1421. wrap(Chart.prototype, 'renderSeries', function (proceed) {
  1422. var series, i = this.series.length;
  1423. if (this.is3d()) {
  1424. while (i--) {
  1425. series = this.series[i];
  1426. series.translate();
  1427. series.render();
  1428. }
  1429. }
  1430. else {
  1431. proceed.call(this);
  1432. }
  1433. });
  1434. addEvent(Chart, 'afterDrawChartBox', function () {
  1435. if (this.is3d()) {
  1436. var chart = this, renderer = chart.renderer, options3d = this.options.chart.options3d, frame = chart.get3dFrame(), xm = this.plotLeft, xp = this.plotLeft + this.plotWidth, ym = this.plotTop, yp = this.plotTop + this.plotHeight, zm = 0, zp = options3d.depth, xmm = xm - (frame.left.visible ? frame.left.size : 0), xpp = xp + (frame.right.visible ? frame.right.size : 0), ymm = ym - (frame.top.visible ? frame.top.size : 0), ypp = yp + (frame.bottom.visible ? frame.bottom.size : 0), zmm = zm - (frame.front.visible ? frame.front.size : 0), zpp = zp + (frame.back.visible ? frame.back.size : 0), verb = chart.hasRendered ? 'animate' : 'attr';
  1437. this.frame3d = frame;
  1438. if (!this.frameShapes) {
  1439. this.frameShapes = {
  1440. bottom: renderer.polyhedron().add(),
  1441. top: renderer.polyhedron().add(),
  1442. left: renderer.polyhedron().add(),
  1443. right: renderer.polyhedron().add(),
  1444. back: renderer.polyhedron().add(),
  1445. front: renderer.polyhedron().add()
  1446. };
  1447. }
  1448. this.frameShapes.bottom[verb]({
  1449. 'class': 'highcharts-3d-frame highcharts-3d-frame-bottom',
  1450. zIndex: frame.bottom.frontFacing ? -1000 : 1000,
  1451. faces: [{
  1452. fill: H.color(frame.bottom.color).brighten(0.1).get(),
  1453. vertexes: [{
  1454. x: xmm,
  1455. y: ypp,
  1456. z: zmm
  1457. }, {
  1458. x: xpp,
  1459. y: ypp,
  1460. z: zmm
  1461. }, {
  1462. x: xpp,
  1463. y: ypp,
  1464. z: zpp
  1465. }, {
  1466. x: xmm,
  1467. y: ypp,
  1468. z: zpp
  1469. }],
  1470. enabled: frame.bottom.visible
  1471. },
  1472. {
  1473. fill: H.color(frame.bottom.color).brighten(0.1).get(),
  1474. vertexes: [{
  1475. x: xm,
  1476. y: yp,
  1477. z: zp
  1478. }, {
  1479. x: xp,
  1480. y: yp,
  1481. z: zp
  1482. }, {
  1483. x: xp,
  1484. y: yp,
  1485. z: zm
  1486. }, {
  1487. x: xm,
  1488. y: yp,
  1489. z: zm
  1490. }],
  1491. enabled: frame.bottom.visible
  1492. },
  1493. {
  1494. fill: H.color(frame.bottom.color).brighten(-0.1).get(),
  1495. vertexes: [{
  1496. x: xmm,
  1497. y: ypp,
  1498. z: zmm
  1499. }, {
  1500. x: xmm,
  1501. y: ypp,
  1502. z: zpp
  1503. }, {
  1504. x: xm,
  1505. y: yp,
  1506. z: zp
  1507. }, {
  1508. x: xm,
  1509. y: yp,
  1510. z: zm
  1511. }],
  1512. enabled: frame.bottom.visible && !frame.left.visible
  1513. },
  1514. {
  1515. fill: H.color(frame.bottom.color).brighten(-0.1).get(),
  1516. vertexes: [{
  1517. x: xpp,
  1518. y: ypp,
  1519. z: zpp
  1520. }, {
  1521. x: xpp,
  1522. y: ypp,
  1523. z: zmm
  1524. }, {
  1525. x: xp,
  1526. y: yp,
  1527. z: zm
  1528. }, {
  1529. x: xp,
  1530. y: yp,
  1531. z: zp
  1532. }],
  1533. enabled: frame.bottom.visible && !frame.right.visible
  1534. },
  1535. {
  1536. fill: H.color(frame.bottom.color).get(),
  1537. vertexes: [{
  1538. x: xpp,
  1539. y: ypp,
  1540. z: zmm
  1541. }, {
  1542. x: xmm,
  1543. y: ypp,
  1544. z: zmm
  1545. }, {
  1546. x: xm,
  1547. y: yp,
  1548. z: zm
  1549. }, {
  1550. x: xp,
  1551. y: yp,
  1552. z: zm
  1553. }],
  1554. enabled: frame.bottom.visible && !frame.front.visible
  1555. },
  1556. {
  1557. fill: H.color(frame.bottom.color).get(),
  1558. vertexes: [{
  1559. x: xmm,
  1560. y: ypp,
  1561. z: zpp
  1562. }, {
  1563. x: xpp,
  1564. y: ypp,
  1565. z: zpp
  1566. }, {
  1567. x: xp,
  1568. y: yp,
  1569. z: zp
  1570. }, {
  1571. x: xm,
  1572. y: yp,
  1573. z: zp
  1574. }],
  1575. enabled: frame.bottom.visible && !frame.back.visible
  1576. }]
  1577. });
  1578. this.frameShapes.top[verb]({
  1579. 'class': 'highcharts-3d-frame highcharts-3d-frame-top',
  1580. zIndex: frame.top.frontFacing ? -1000 : 1000,
  1581. faces: [{
  1582. fill: H.color(frame.top.color).brighten(0.1).get(),
  1583. vertexes: [{
  1584. x: xmm,
  1585. y: ymm,
  1586. z: zpp
  1587. }, {
  1588. x: xpp,
  1589. y: ymm,
  1590. z: zpp
  1591. }, {
  1592. x: xpp,
  1593. y: ymm,
  1594. z: zmm
  1595. }, {
  1596. x: xmm,
  1597. y: ymm,
  1598. z: zmm
  1599. }],
  1600. enabled: frame.top.visible
  1601. },
  1602. {
  1603. fill: H.color(frame.top.color).brighten(0.1).get(),
  1604. vertexes: [{
  1605. x: xm,
  1606. y: ym,
  1607. z: zm
  1608. }, {
  1609. x: xp,
  1610. y: ym,
  1611. z: zm
  1612. }, {
  1613. x: xp,
  1614. y: ym,
  1615. z: zp
  1616. }, {
  1617. x: xm,
  1618. y: ym,
  1619. z: zp
  1620. }],
  1621. enabled: frame.top.visible
  1622. },
  1623. {
  1624. fill: H.color(frame.top.color).brighten(-0.1).get(),
  1625. vertexes: [{
  1626. x: xmm,
  1627. y: ymm,
  1628. z: zpp
  1629. }, {
  1630. x: xmm,
  1631. y: ymm,
  1632. z: zmm
  1633. }, {
  1634. x: xm,
  1635. y: ym,
  1636. z: zm
  1637. }, {
  1638. x: xm,
  1639. y: ym,
  1640. z: zp
  1641. }],
  1642. enabled: frame.top.visible && !frame.left.visible
  1643. },
  1644. {
  1645. fill: H.color(frame.top.color).brighten(-0.1).get(),
  1646. vertexes: [{
  1647. x: xpp,
  1648. y: ymm,
  1649. z: zmm
  1650. }, {
  1651. x: xpp,
  1652. y: ymm,
  1653. z: zpp
  1654. }, {
  1655. x: xp,
  1656. y: ym,
  1657. z: zp
  1658. }, {
  1659. x: xp,
  1660. y: ym,
  1661. z: zm
  1662. }],
  1663. enabled: frame.top.visible && !frame.right.visible
  1664. },
  1665. {
  1666. fill: H.color(frame.top.color).get(),
  1667. vertexes: [{
  1668. x: xmm,
  1669. y: ymm,
  1670. z: zmm
  1671. }, {
  1672. x: xpp,
  1673. y: ymm,
  1674. z: zmm
  1675. }, {
  1676. x: xp,
  1677. y: ym,
  1678. z: zm
  1679. }, {
  1680. x: xm,
  1681. y: ym,
  1682. z: zm
  1683. }],
  1684. enabled: frame.top.visible && !frame.front.visible
  1685. },
  1686. {
  1687. fill: H.color(frame.top.color).get(),
  1688. vertexes: [{
  1689. x: xpp,
  1690. y: ymm,
  1691. z: zpp
  1692. }, {
  1693. x: xmm,
  1694. y: ymm,
  1695. z: zpp
  1696. }, {
  1697. x: xm,
  1698. y: ym,
  1699. z: zp
  1700. }, {
  1701. x: xp,
  1702. y: ym,
  1703. z: zp
  1704. }],
  1705. enabled: frame.top.visible && !frame.back.visible
  1706. }]
  1707. });
  1708. this.frameShapes.left[verb]({
  1709. 'class': 'highcharts-3d-frame highcharts-3d-frame-left',
  1710. zIndex: frame.left.frontFacing ? -1000 : 1000,
  1711. faces: [{
  1712. fill: H.color(frame.left.color).brighten(0.1).get(),
  1713. vertexes: [{
  1714. x: xmm,
  1715. y: ypp,
  1716. z: zmm
  1717. }, {
  1718. x: xm,
  1719. y: yp,
  1720. z: zm
  1721. }, {
  1722. x: xm,
  1723. y: yp,
  1724. z: zp
  1725. }, {
  1726. x: xmm,
  1727. y: ypp,
  1728. z: zpp
  1729. }],
  1730. enabled: frame.left.visible && !frame.bottom.visible
  1731. },
  1732. {
  1733. fill: H.color(frame.left.color).brighten(0.1).get(),
  1734. vertexes: [{
  1735. x: xmm,
  1736. y: ymm,
  1737. z: zpp
  1738. }, {
  1739. x: xm,
  1740. y: ym,
  1741. z: zp
  1742. }, {
  1743. x: xm,
  1744. y: ym,
  1745. z: zm
  1746. }, {
  1747. x: xmm,
  1748. y: ymm,
  1749. z: zmm
  1750. }],
  1751. enabled: frame.left.visible && !frame.top.visible
  1752. },
  1753. {
  1754. fill: H.color(frame.left.color).brighten(-0.1).get(),
  1755. vertexes: [{
  1756. x: xmm,
  1757. y: ypp,
  1758. z: zpp
  1759. }, {
  1760. x: xmm,
  1761. y: ymm,
  1762. z: zpp
  1763. }, {
  1764. x: xmm,
  1765. y: ymm,
  1766. z: zmm
  1767. }, {
  1768. x: xmm,
  1769. y: ypp,
  1770. z: zmm
  1771. }],
  1772. enabled: frame.left.visible
  1773. },
  1774. {
  1775. fill: H.color(frame.left.color).brighten(-0.1).get(),
  1776. vertexes: [{
  1777. x: xm,
  1778. y: ym,
  1779. z: zp
  1780. }, {
  1781. x: xm,
  1782. y: yp,
  1783. z: zp
  1784. }, {
  1785. x: xm,
  1786. y: yp,
  1787. z: zm
  1788. }, {
  1789. x: xm,
  1790. y: ym,
  1791. z: zm
  1792. }],
  1793. enabled: frame.left.visible
  1794. },
  1795. {
  1796. fill: H.color(frame.left.color).get(),
  1797. vertexes: [{
  1798. x: xmm,
  1799. y: ypp,
  1800. z: zmm
  1801. }, {
  1802. x: xmm,
  1803. y: ymm,
  1804. z: zmm
  1805. }, {
  1806. x: xm,
  1807. y: ym,
  1808. z: zm
  1809. }, {
  1810. x: xm,
  1811. y: yp,
  1812. z: zm
  1813. }],
  1814. enabled: frame.left.visible && !frame.front.visible
  1815. },
  1816. {
  1817. fill: H.color(frame.left.color).get(),
  1818. vertexes: [{
  1819. x: xmm,
  1820. y: ymm,
  1821. z: zpp
  1822. }, {
  1823. x: xmm,
  1824. y: ypp,
  1825. z: zpp
  1826. }, {
  1827. x: xm,
  1828. y: yp,
  1829. z: zp
  1830. }, {
  1831. x: xm,
  1832. y: ym,
  1833. z: zp
  1834. }],
  1835. enabled: frame.left.visible && !frame.back.visible
  1836. }]
  1837. });
  1838. this.frameShapes.right[verb]({
  1839. 'class': 'highcharts-3d-frame highcharts-3d-frame-right',
  1840. zIndex: frame.right.frontFacing ? -1000 : 1000,
  1841. faces: [{
  1842. fill: H.color(frame.right.color).brighten(0.1).get(),
  1843. vertexes: [{
  1844. x: xpp,
  1845. y: ypp,
  1846. z: zpp
  1847. }, {
  1848. x: xp,
  1849. y: yp,
  1850. z: zp
  1851. }, {
  1852. x: xp,
  1853. y: yp,
  1854. z: zm
  1855. }, {
  1856. x: xpp,
  1857. y: ypp,
  1858. z: zmm
  1859. }],
  1860. enabled: frame.right.visible && !frame.bottom.visible
  1861. },
  1862. {
  1863. fill: H.color(frame.right.color).brighten(0.1).get(),
  1864. vertexes: [{
  1865. x: xpp,
  1866. y: ymm,
  1867. z: zmm
  1868. }, {
  1869. x: xp,
  1870. y: ym,
  1871. z: zm
  1872. }, {
  1873. x: xp,
  1874. y: ym,
  1875. z: zp
  1876. }, {
  1877. x: xpp,
  1878. y: ymm,
  1879. z: zpp
  1880. }],
  1881. enabled: frame.right.visible && !frame.top.visible
  1882. },
  1883. {
  1884. fill: H.color(frame.right.color).brighten(-0.1).get(),
  1885. vertexes: [{
  1886. x: xp,
  1887. y: ym,
  1888. z: zm
  1889. }, {
  1890. x: xp,
  1891. y: yp,
  1892. z: zm
  1893. }, {
  1894. x: xp,
  1895. y: yp,
  1896. z: zp
  1897. }, {
  1898. x: xp,
  1899. y: ym,
  1900. z: zp
  1901. }],
  1902. enabled: frame.right.visible
  1903. },
  1904. {
  1905. fill: H.color(frame.right.color).brighten(-0.1).get(),
  1906. vertexes: [{
  1907. x: xpp,
  1908. y: ypp,
  1909. z: zmm
  1910. }, {
  1911. x: xpp,
  1912. y: ymm,
  1913. z: zmm
  1914. }, {
  1915. x: xpp,
  1916. y: ymm,
  1917. z: zpp
  1918. }, {
  1919. x: xpp,
  1920. y: ypp,
  1921. z: zpp
  1922. }],
  1923. enabled: frame.right.visible
  1924. },
  1925. {
  1926. fill: H.color(frame.right.color).get(),
  1927. vertexes: [{
  1928. x: xpp,
  1929. y: ymm,
  1930. z: zmm
  1931. }, {
  1932. x: xpp,
  1933. y: ypp,
  1934. z: zmm
  1935. }, {
  1936. x: xp,
  1937. y: yp,
  1938. z: zm
  1939. }, {
  1940. x: xp,
  1941. y: ym,
  1942. z: zm
  1943. }],
  1944. enabled: frame.right.visible && !frame.front.visible
  1945. },
  1946. {
  1947. fill: H.color(frame.right.color).get(),
  1948. vertexes: [{
  1949. x: xpp,
  1950. y: ypp,
  1951. z: zpp
  1952. }, {
  1953. x: xpp,
  1954. y: ymm,
  1955. z: zpp
  1956. }, {
  1957. x: xp,
  1958. y: ym,
  1959. z: zp
  1960. }, {
  1961. x: xp,
  1962. y: yp,
  1963. z: zp
  1964. }],
  1965. enabled: frame.right.visible && !frame.back.visible
  1966. }]
  1967. });
  1968. this.frameShapes.back[verb]({
  1969. 'class': 'highcharts-3d-frame highcharts-3d-frame-back',
  1970. zIndex: frame.back.frontFacing ? -1000 : 1000,
  1971. faces: [{
  1972. fill: H.color(frame.back.color).brighten(0.1).get(),
  1973. vertexes: [{
  1974. x: xpp,
  1975. y: ypp,
  1976. z: zpp
  1977. }, {
  1978. x: xmm,
  1979. y: ypp,
  1980. z: zpp
  1981. }, {
  1982. x: xm,
  1983. y: yp,
  1984. z: zp
  1985. }, {
  1986. x: xp,
  1987. y: yp,
  1988. z: zp
  1989. }],
  1990. enabled: frame.back.visible && !frame.bottom.visible
  1991. },
  1992. {
  1993. fill: H.color(frame.back.color).brighten(0.1).get(),
  1994. vertexes: [{
  1995. x: xmm,
  1996. y: ymm,
  1997. z: zpp
  1998. }, {
  1999. x: xpp,
  2000. y: ymm,
  2001. z: zpp
  2002. }, {
  2003. x: xp,
  2004. y: ym,
  2005. z: zp
  2006. }, {
  2007. x: xm,
  2008. y: ym,
  2009. z: zp
  2010. }],
  2011. enabled: frame.back.visible && !frame.top.visible
  2012. },
  2013. {
  2014. fill: H.color(frame.back.color).brighten(-0.1).get(),
  2015. vertexes: [{
  2016. x: xmm,
  2017. y: ypp,
  2018. z: zpp
  2019. }, {
  2020. x: xmm,
  2021. y: ymm,
  2022. z: zpp
  2023. }, {
  2024. x: xm,
  2025. y: ym,
  2026. z: zp
  2027. }, {
  2028. x: xm,
  2029. y: yp,
  2030. z: zp
  2031. }],
  2032. enabled: frame.back.visible && !frame.left.visible
  2033. },
  2034. {
  2035. fill: H.color(frame.back.color).brighten(-0.1).get(),
  2036. vertexes: [{
  2037. x: xpp,
  2038. y: ymm,
  2039. z: zpp
  2040. }, {
  2041. x: xpp,
  2042. y: ypp,
  2043. z: zpp
  2044. }, {
  2045. x: xp,
  2046. y: yp,
  2047. z: zp
  2048. }, {
  2049. x: xp,
  2050. y: ym,
  2051. z: zp
  2052. }],
  2053. enabled: frame.back.visible && !frame.right.visible
  2054. },
  2055. {
  2056. fill: H.color(frame.back.color).get(),
  2057. vertexes: [{
  2058. x: xm,
  2059. y: ym,
  2060. z: zp
  2061. }, {
  2062. x: xp,
  2063. y: ym,
  2064. z: zp
  2065. }, {
  2066. x: xp,
  2067. y: yp,
  2068. z: zp
  2069. }, {
  2070. x: xm,
  2071. y: yp,
  2072. z: zp
  2073. }],
  2074. enabled: frame.back.visible
  2075. },
  2076. {
  2077. fill: H.color(frame.back.color).get(),
  2078. vertexes: [{
  2079. x: xmm,
  2080. y: ypp,
  2081. z: zpp
  2082. }, {
  2083. x: xpp,
  2084. y: ypp,
  2085. z: zpp
  2086. }, {
  2087. x: xpp,
  2088. y: ymm,
  2089. z: zpp
  2090. }, {
  2091. x: xmm,
  2092. y: ymm,
  2093. z: zpp
  2094. }],
  2095. enabled: frame.back.visible
  2096. }]
  2097. });
  2098. this.frameShapes.front[verb]({
  2099. 'class': 'highcharts-3d-frame highcharts-3d-frame-front',
  2100. zIndex: frame.front.frontFacing ? -1000 : 1000,
  2101. faces: [{
  2102. fill: H.color(frame.front.color).brighten(0.1).get(),
  2103. vertexes: [{
  2104. x: xmm,
  2105. y: ypp,
  2106. z: zmm
  2107. }, {
  2108. x: xpp,
  2109. y: ypp,
  2110. z: zmm
  2111. }, {
  2112. x: xp,
  2113. y: yp,
  2114. z: zm
  2115. }, {
  2116. x: xm,
  2117. y: yp,
  2118. z: zm
  2119. }],
  2120. enabled: frame.front.visible && !frame.bottom.visible
  2121. },
  2122. {
  2123. fill: H.color(frame.front.color).brighten(0.1).get(),
  2124. vertexes: [{
  2125. x: xpp,
  2126. y: ymm,
  2127. z: zmm
  2128. }, {
  2129. x: xmm,
  2130. y: ymm,
  2131. z: zmm
  2132. }, {
  2133. x: xm,
  2134. y: ym,
  2135. z: zm
  2136. }, {
  2137. x: xp,
  2138. y: ym,
  2139. z: zm
  2140. }],
  2141. enabled: frame.front.visible && !frame.top.visible
  2142. },
  2143. {
  2144. fill: H.color(frame.front.color).brighten(-0.1).get(),
  2145. vertexes: [{
  2146. x: xmm,
  2147. y: ymm,
  2148. z: zmm
  2149. }, {
  2150. x: xmm,
  2151. y: ypp,
  2152. z: zmm
  2153. }, {
  2154. x: xm,
  2155. y: yp,
  2156. z: zm
  2157. }, {
  2158. x: xm,
  2159. y: ym,
  2160. z: zm
  2161. }],
  2162. enabled: frame.front.visible && !frame.left.visible
  2163. },
  2164. {
  2165. fill: H.color(frame.front.color).brighten(-0.1).get(),
  2166. vertexes: [{
  2167. x: xpp,
  2168. y: ypp,
  2169. z: zmm
  2170. }, {
  2171. x: xpp,
  2172. y: ymm,
  2173. z: zmm
  2174. }, {
  2175. x: xp,
  2176. y: ym,
  2177. z: zm
  2178. }, {
  2179. x: xp,
  2180. y: yp,
  2181. z: zm
  2182. }],
  2183. enabled: frame.front.visible && !frame.right.visible
  2184. },
  2185. {
  2186. fill: H.color(frame.front.color).get(),
  2187. vertexes: [{
  2188. x: xp,
  2189. y: ym,
  2190. z: zm
  2191. }, {
  2192. x: xm,
  2193. y: ym,
  2194. z: zm
  2195. }, {
  2196. x: xm,
  2197. y: yp,
  2198. z: zm
  2199. }, {
  2200. x: xp,
  2201. y: yp,
  2202. z: zm
  2203. }],
  2204. enabled: frame.front.visible
  2205. },
  2206. {
  2207. fill: H.color(frame.front.color).get(),
  2208. vertexes: [{
  2209. x: xpp,
  2210. y: ypp,
  2211. z: zmm
  2212. }, {
  2213. x: xmm,
  2214. y: ypp,
  2215. z: zmm
  2216. }, {
  2217. x: xmm,
  2218. y: ymm,
  2219. z: zmm
  2220. }, {
  2221. x: xpp,
  2222. y: ymm,
  2223. z: zmm
  2224. }],
  2225. enabled: frame.front.visible
  2226. }]
  2227. });
  2228. }
  2229. });
  2230. Chart.prototype.retrieveStacks = function (stacking) {
  2231. var series = this.series, stacks = {}, stackNumber, i = 1;
  2232. this.series.forEach(function (s) {
  2233. stackNumber = pick(s.options.stack, (stacking ? 0 : series.length - 1 - s.index)); // #3841, #4532
  2234. if (!stacks[stackNumber]) {
  2235. stacks[stackNumber] = { series: [s], position: i };
  2236. i++;
  2237. }
  2238. else {
  2239. stacks[stackNumber].series.push(s);
  2240. }
  2241. });
  2242. stacks.totalStacks = i + 1;
  2243. return stacks;
  2244. };
  2245. Chart.prototype.get3dFrame = function () {
  2246. var chart = this, options3d = chart.options.chart.options3d, frameOptions = options3d.frame, xm = chart.plotLeft, xp = chart.plotLeft + chart.plotWidth, ym = chart.plotTop, yp = chart.plotTop + chart.plotHeight, zm = 0, zp = options3d.depth, faceOrientation = function (vertexes) {
  2247. var area = H.shapeArea3d(vertexes, chart);
  2248. // Give it 0.5 squared-pixel as a margin for rounding errors.
  2249. if (area > 0.5) {
  2250. return 1;
  2251. }
  2252. if (area < -0.5) {
  2253. return -1;
  2254. }
  2255. return 0;
  2256. }, bottomOrientation = faceOrientation([
  2257. { x: xm, y: yp, z: zp },
  2258. { x: xp, y: yp, z: zp },
  2259. { x: xp, y: yp, z: zm },
  2260. { x: xm, y: yp, z: zm }
  2261. ]), topOrientation = faceOrientation([
  2262. { x: xm, y: ym, z: zm },
  2263. { x: xp, y: ym, z: zm },
  2264. { x: xp, y: ym, z: zp },
  2265. { x: xm, y: ym, z: zp }
  2266. ]), leftOrientation = faceOrientation([
  2267. { x: xm, y: ym, z: zm },
  2268. { x: xm, y: ym, z: zp },
  2269. { x: xm, y: yp, z: zp },
  2270. { x: xm, y: yp, z: zm }
  2271. ]), rightOrientation = faceOrientation([
  2272. { x: xp, y: ym, z: zp },
  2273. { x: xp, y: ym, z: zm },
  2274. { x: xp, y: yp, z: zm },
  2275. { x: xp, y: yp, z: zp }
  2276. ]), frontOrientation = faceOrientation([
  2277. { x: xm, y: yp, z: zm },
  2278. { x: xp, y: yp, z: zm },
  2279. { x: xp, y: ym, z: zm },
  2280. { x: xm, y: ym, z: zm }
  2281. ]), backOrientation = faceOrientation([
  2282. { x: xm, y: ym, z: zp },
  2283. { x: xp, y: ym, z: zp },
  2284. { x: xp, y: yp, z: zp },
  2285. { x: xm, y: yp, z: zp }
  2286. ]), defaultShowBottom = false, defaultShowTop = false, defaultShowLeft = false, defaultShowRight = false, defaultShowFront = false, defaultShowBack = true;
  2287. // The 'default' criteria to visible faces of the frame is looking up every
  2288. // axis to decide whenever the left/right//top/bottom sides of the frame
  2289. // will be shown
  2290. []
  2291. .concat(chart.xAxis, chart.yAxis, chart.zAxis)
  2292. .forEach(function (axis) {
  2293. if (axis) {
  2294. if (axis.horiz) {
  2295. if (axis.opposite) {
  2296. defaultShowTop = true;
  2297. }
  2298. else {
  2299. defaultShowBottom = true;
  2300. }
  2301. }
  2302. else {
  2303. if (axis.opposite) {
  2304. defaultShowRight = true;
  2305. }
  2306. else {
  2307. defaultShowLeft = true;
  2308. }
  2309. }
  2310. }
  2311. });
  2312. var getFaceOptions = function (sources, faceOrientation, defaultVisible) {
  2313. var faceAttrs = ['size', 'color', 'visible'];
  2314. var options = {};
  2315. for (var i = 0; i < faceAttrs.length; i++) {
  2316. var attr = faceAttrs[i];
  2317. for (var j = 0; j < sources.length; j++) {
  2318. if (typeof sources[j] === 'object') {
  2319. var val = sources[j][attr];
  2320. if (typeof val !== 'undefined' && val !== null) {
  2321. options[attr] = val;
  2322. break;
  2323. }
  2324. }
  2325. }
  2326. }
  2327. var isVisible = defaultVisible;
  2328. if (options.visible === true || options.visible === false) {
  2329. isVisible = options.visible;
  2330. }
  2331. else if (options.visible === 'auto') {
  2332. isVisible = faceOrientation > 0;
  2333. }
  2334. return {
  2335. size: pick(options.size, 1),
  2336. color: pick(options.color, 'none'),
  2337. frontFacing: faceOrientation > 0,
  2338. visible: isVisible
  2339. };
  2340. };
  2341. // docs @TODO: Add all frame options (left, right, top, bottom, front, back)
  2342. // to apioptions JSDoc once the new system is up.
  2343. var ret = {
  2344. axes: {},
  2345. // FIXME: Previously, left/right, top/bottom and front/back pairs shared
  2346. // size and color.
  2347. // For compatibility and consistency sake, when one face have
  2348. // size/color/visibility set, the opposite face will default to the same
  2349. // values. Also, left/right used to be called 'side', so that's also
  2350. // added as a fallback
  2351. bottom: getFaceOptions([frameOptions.bottom, frameOptions.top, frameOptions], bottomOrientation, defaultShowBottom),
  2352. top: getFaceOptions([frameOptions.top, frameOptions.bottom, frameOptions], topOrientation, defaultShowTop),
  2353. left: getFaceOptions([
  2354. frameOptions.left,
  2355. frameOptions.right,
  2356. frameOptions.side,
  2357. frameOptions
  2358. ], leftOrientation, defaultShowLeft),
  2359. right: getFaceOptions([
  2360. frameOptions.right,
  2361. frameOptions.left,
  2362. frameOptions.side,
  2363. frameOptions
  2364. ], rightOrientation, defaultShowRight),
  2365. back: getFaceOptions([frameOptions.back, frameOptions.front, frameOptions], backOrientation, defaultShowBack),
  2366. front: getFaceOptions([frameOptions.front, frameOptions.back, frameOptions], frontOrientation, defaultShowFront)
  2367. };
  2368. // Decide the bast place to put axis title/labels based on the visible
  2369. // faces. Ideally, The labels can only be on the edge between a visible face
  2370. // and an invisble one. Also, the Y label should be one the left-most edge
  2371. // (right-most if opposite),
  2372. if (options3d.axisLabelPosition === 'auto') {
  2373. var isValidEdge = function (face1, face2) {
  2374. return ((face1.visible !== face2.visible) ||
  2375. (face1.visible &&
  2376. face2.visible &&
  2377. (face1.frontFacing !== face2.frontFacing)));
  2378. };
  2379. var yEdges = [];
  2380. if (isValidEdge(ret.left, ret.front)) {
  2381. yEdges.push({
  2382. y: (ym + yp) / 2,
  2383. x: xm,
  2384. z: zm,
  2385. xDir: { x: 1, y: 0, z: 0 }
  2386. });
  2387. }
  2388. if (isValidEdge(ret.left, ret.back)) {
  2389. yEdges.push({
  2390. y: (ym + yp) / 2,
  2391. x: xm,
  2392. z: zp,
  2393. xDir: { x: 0, y: 0, z: -1 }
  2394. });
  2395. }
  2396. if (isValidEdge(ret.right, ret.front)) {
  2397. yEdges.push({
  2398. y: (ym + yp) / 2,
  2399. x: xp,
  2400. z: zm,
  2401. xDir: { x: 0, y: 0, z: 1 }
  2402. });
  2403. }
  2404. if (isValidEdge(ret.right, ret.back)) {
  2405. yEdges.push({
  2406. y: (ym + yp) / 2,
  2407. x: xp,
  2408. z: zp,
  2409. xDir: { x: -1, y: 0, z: 0 }
  2410. });
  2411. }
  2412. var xBottomEdges = [];
  2413. if (isValidEdge(ret.bottom, ret.front)) {
  2414. xBottomEdges.push({
  2415. x: (xm + xp) / 2,
  2416. y: yp,
  2417. z: zm,
  2418. xDir: { x: 1, y: 0, z: 0 }
  2419. });
  2420. }
  2421. if (isValidEdge(ret.bottom, ret.back)) {
  2422. xBottomEdges.push({
  2423. x: (xm + xp) / 2,
  2424. y: yp,
  2425. z: zp,
  2426. xDir: { x: -1, y: 0, z: 0 }
  2427. });
  2428. }
  2429. var xTopEdges = [];
  2430. if (isValidEdge(ret.top, ret.front)) {
  2431. xTopEdges.push({
  2432. x: (xm + xp) / 2,
  2433. y: ym,
  2434. z: zm,
  2435. xDir: { x: 1, y: 0, z: 0 }
  2436. });
  2437. }
  2438. if (isValidEdge(ret.top, ret.back)) {
  2439. xTopEdges.push({
  2440. x: (xm + xp) / 2,
  2441. y: ym,
  2442. z: zp,
  2443. xDir: { x: -1, y: 0, z: 0 }
  2444. });
  2445. }
  2446. var zBottomEdges = [];
  2447. if (isValidEdge(ret.bottom, ret.left)) {
  2448. zBottomEdges.push({
  2449. z: (zm + zp) / 2,
  2450. y: yp,
  2451. x: xm,
  2452. xDir: { x: 0, y: 0, z: -1 }
  2453. });
  2454. }
  2455. if (isValidEdge(ret.bottom, ret.right)) {
  2456. zBottomEdges.push({
  2457. z: (zm + zp) / 2,
  2458. y: yp,
  2459. x: xp,
  2460. xDir: { x: 0, y: 0, z: 1 }
  2461. });
  2462. }
  2463. var zTopEdges = [];
  2464. if (isValidEdge(ret.top, ret.left)) {
  2465. zTopEdges.push({
  2466. z: (zm + zp) / 2,
  2467. y: ym,
  2468. x: xm,
  2469. xDir: { x: 0, y: 0, z: -1 }
  2470. });
  2471. }
  2472. if (isValidEdge(ret.top, ret.right)) {
  2473. zTopEdges.push({
  2474. z: (zm + zp) / 2,
  2475. y: ym,
  2476. x: xp,
  2477. xDir: { x: 0, y: 0, z: 1 }
  2478. });
  2479. }
  2480. var pickEdge = function (edges, axis, mult) {
  2481. if (edges.length === 0) {
  2482. return null;
  2483. }
  2484. if (edges.length === 1) {
  2485. return edges[0];
  2486. }
  2487. var best = 0, projections = perspective(edges, chart, false);
  2488. for (var i = 1; i < projections.length; i++) {
  2489. if (mult * projections[i][axis] >
  2490. mult * projections[best][axis]) {
  2491. best = i;
  2492. }
  2493. else if ((mult * projections[i][axis] ===
  2494. mult * projections[best][axis]) &&
  2495. (projections[i].z < projections[best].z)) {
  2496. best = i;
  2497. }
  2498. }
  2499. return edges[best];
  2500. };
  2501. ret.axes = {
  2502. y: {
  2503. 'left': pickEdge(yEdges, 'x', -1),
  2504. 'right': pickEdge(yEdges, 'x', +1)
  2505. },
  2506. x: {
  2507. 'top': pickEdge(xTopEdges, 'y', -1),
  2508. 'bottom': pickEdge(xBottomEdges, 'y', +1)
  2509. },
  2510. z: {
  2511. 'top': pickEdge(zTopEdges, 'y', -1),
  2512. 'bottom': pickEdge(zBottomEdges, 'y', +1)
  2513. }
  2514. };
  2515. }
  2516. else {
  2517. ret.axes = {
  2518. y: {
  2519. 'left': { x: xm, z: zm, xDir: { x: 1, y: 0, z: 0 } },
  2520. 'right': { x: xp, z: zm, xDir: { x: 0, y: 0, z: 1 } }
  2521. },
  2522. x: {
  2523. 'top': { y: ym, z: zm, xDir: { x: 1, y: 0, z: 0 } },
  2524. 'bottom': { y: yp, z: zm, xDir: { x: 1, y: 0, z: 0 } }
  2525. },
  2526. z: {
  2527. 'top': {
  2528. x: defaultShowLeft ? xp : xm,
  2529. y: ym,
  2530. xDir: defaultShowLeft ?
  2531. { x: 0, y: 0, z: 1 } :
  2532. { x: 0, y: 0, z: -1 }
  2533. },
  2534. 'bottom': {
  2535. x: defaultShowLeft ? xp : xm,
  2536. y: yp,
  2537. xDir: defaultShowLeft ?
  2538. { x: 0, y: 0, z: 1 } :
  2539. { x: 0, y: 0, z: -1 }
  2540. }
  2541. }
  2542. };
  2543. }
  2544. return ret;
  2545. };
  2546. // Animation setter for matrix property.
  2547. H.Fx.prototype.matrixSetter = function () {
  2548. var interpolated;
  2549. if (this.pos < 1 &&
  2550. (isArray(this.start) || isArray(this.end))) {
  2551. var start = this.start || [1, 0, 0, 1, 0, 0];
  2552. var end = this.end || [1, 0, 0, 1, 0, 0];
  2553. interpolated = [];
  2554. for (var i = 0; i < 6; i++) {
  2555. interpolated.push(this.pos * end[i] + (1 - this.pos) * start[i]);
  2556. }
  2557. }
  2558. else {
  2559. interpolated = this.end;
  2560. }
  2561. this.elem.attr(this.prop, interpolated, null, true);
  2562. };
  2563. /**
  2564. * Note: As of v5.0.12, `frame.left` or `frame.right` should be used instead.
  2565. *
  2566. * The side for the frame around a 3D chart.
  2567. *
  2568. * @deprecated
  2569. * @since 4.0
  2570. * @product highcharts
  2571. * @requires highcharts-3d
  2572. * @apioption chart.options3d.frame.side
  2573. */
  2574. /**
  2575. * The color of the panel.
  2576. *
  2577. * @deprecated
  2578. * @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
  2579. * @default transparent
  2580. * @since 4.0
  2581. * @product highcharts
  2582. * @apioption chart.options3d.frame.side.color
  2583. */
  2584. /**
  2585. * The thickness of the panel.
  2586. *
  2587. * @deprecated
  2588. * @type {number}
  2589. * @default 1
  2590. * @since 4.0
  2591. * @product highcharts
  2592. * @apioption chart.options3d.frame.side.size
  2593. */
  2594. ''; // adds doclets above to transpiled file
  2595. });
  2596. _registerModule(_modules, 'parts-3d/Axis.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  2597. /* *
  2598. *
  2599. * (c) 2010-2019 Torstein Honsi
  2600. *
  2601. * Extenstion for 3d axes
  2602. *
  2603. * License: www.highcharts.com/license
  2604. *
  2605. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  2606. *
  2607. * */
  2608. var extend = U.extend, pick = U.pick, splat = U.splat, wrap = U.wrap;
  2609. var ZAxis, addEvent = H.addEvent, Axis = H.Axis, Chart = H.Chart, deg2rad = H.deg2rad, merge = H.merge, perspective = H.perspective, perspective3D = H.perspective3D, shapeArea = H.shapeArea, Tick = H.Tick;
  2610. /**
  2611. * @optionparent xAxis
  2612. */
  2613. var extendedOptions = {
  2614. labels: {
  2615. /**
  2616. * Defines how the labels are be repositioned according to the 3D chart
  2617. * orientation.
  2618. *
  2619. * - `'offset'`: Maintain a fixed horizontal/vertical distance from the
  2620. * tick marks, despite the chart orientation. This is the backwards
  2621. * compatible behavior, and causes skewing of X and Z axes.
  2622. *
  2623. * - `'chart'`: Preserve 3D position relative to the chart.
  2624. * This looks nice, but hard to read if the text isn't
  2625. * forward-facing.
  2626. *
  2627. * - `'flap'`: Rotated text along the axis to compensate for the chart
  2628. * orientation. This tries to maintain text as legible as possible
  2629. * on all orientations.
  2630. *
  2631. * - `'ortho'`: Rotated text along the axis direction so that the labels
  2632. * are orthogonal to the axis. This is very similar to `'flap'`,
  2633. * but prevents skewing the labels (X and Y scaling are still
  2634. * present).
  2635. *
  2636. * @sample highcharts/3d/skewed-labels/
  2637. * Skewed labels
  2638. *
  2639. * @since 5.0.15
  2640. * @validvalue ['offset', 'chart', 'flap', 'ortho']
  2641. * @product highcharts
  2642. * @requires highcharts-3d
  2643. */
  2644. position3d: 'offset',
  2645. /**
  2646. * If enabled, the axis labels will skewed to follow the perspective.
  2647. *
  2648. * This will fix overlapping labels and titles, but texts become less
  2649. * legible due to the distortion.
  2650. *
  2651. * The final appearance depends heavily on `labels.position3d`.
  2652. *
  2653. * @sample highcharts/3d/skewed-labels/
  2654. * Skewed labels
  2655. *
  2656. * @since 5.0.15
  2657. * @product highcharts
  2658. * @requires highcharts-3d
  2659. */
  2660. skew3d: false
  2661. },
  2662. title: {
  2663. /**
  2664. * Defines how the title is repositioned according to the 3D chart
  2665. * orientation.
  2666. *
  2667. * - `'offset'`: Maintain a fixed horizontal/vertical distance from the
  2668. * tick marks, despite the chart orientation. This is the backwards
  2669. * compatible behavior, and causes skewing of X and Z axes.
  2670. *
  2671. * - `'chart'`: Preserve 3D position relative to the chart.
  2672. * This looks nice, but hard to read if the text isn't
  2673. * forward-facing.
  2674. *
  2675. * - `'flap'`: Rotated text along the axis to compensate for the chart
  2676. * orientation. This tries to maintain text as legible as possible on
  2677. * all orientations.
  2678. *
  2679. * - `'ortho'`: Rotated text along the axis direction so that the labels
  2680. * are orthogonal to the axis. This is very similar to `'flap'`, but
  2681. * prevents skewing the labels (X and Y scaling are still present).
  2682. *
  2683. * - `undefined`: Will use the config from `labels.position3d`
  2684. *
  2685. * @sample highcharts/3d/skewed-labels/
  2686. * Skewed labels
  2687. *
  2688. * @type {"offset"|"chart"|"flap"|"ortho"|null}
  2689. * @since 5.0.15
  2690. * @product highcharts
  2691. * @requires highcharts-3d
  2692. */
  2693. position3d: null,
  2694. /**
  2695. * If enabled, the axis title will skewed to follow the perspective.
  2696. *
  2697. * This will fix overlapping labels and titles, but texts become less
  2698. * legible due to the distortion.
  2699. *
  2700. * The final appearance depends heavily on `title.position3d`.
  2701. *
  2702. * A `null` value will use the config from `labels.skew3d`.
  2703. *
  2704. * @sample highcharts/3d/skewed-labels/
  2705. * Skewed labels
  2706. *
  2707. * @type {boolean|null}
  2708. * @since 5.0.15
  2709. * @product highcharts
  2710. * @requires highcharts-3d
  2711. */
  2712. skew3d: null
  2713. }
  2714. };
  2715. /* eslint-disable no-invalid-this */
  2716. merge(true, Axis.prototype.defaultOptions, extendedOptions);
  2717. addEvent(Axis, 'afterSetOptions', function () {
  2718. var options;
  2719. if (this.chart.is3d && this.chart.is3d() && this.coll !== 'colorAxis') {
  2720. options = this.options;
  2721. options.tickWidth = pick(options.tickWidth, 0);
  2722. options.gridLineWidth = pick(options.gridLineWidth, 1);
  2723. }
  2724. });
  2725. wrap(Axis.prototype, 'getPlotLinePath', function (proceed) {
  2726. var path = proceed.apply(this, [].slice.call(arguments, 1));
  2727. // Do not do this if the chart is not 3D
  2728. if (!this.chart.is3d() || this.coll === 'colorAxis') {
  2729. return path;
  2730. }
  2731. if (path === null) {
  2732. return path;
  2733. }
  2734. var chart = this.chart, options3d = chart.options.chart.options3d, d = this.isZAxis ? chart.plotWidth : options3d.depth, frame = chart.frame3d;
  2735. var pArr = [
  2736. this.swapZ({ x: path[1], y: path[2], z: 0 }),
  2737. this.swapZ({ x: path[1], y: path[2], z: d }),
  2738. this.swapZ({ x: path[4], y: path[5], z: 0 }),
  2739. this.swapZ({ x: path[4], y: path[5], z: d })
  2740. ];
  2741. var pathSegments = [];
  2742. if (!this.horiz) { // Y-Axis
  2743. if (frame.front.visible) {
  2744. pathSegments.push(pArr[0], pArr[2]);
  2745. }
  2746. if (frame.back.visible) {
  2747. pathSegments.push(pArr[1], pArr[3]);
  2748. }
  2749. if (frame.left.visible) {
  2750. pathSegments.push(pArr[0], pArr[1]);
  2751. }
  2752. if (frame.right.visible) {
  2753. pathSegments.push(pArr[2], pArr[3]);
  2754. }
  2755. }
  2756. else if (this.isZAxis) { // Z-Axis
  2757. if (frame.left.visible) {
  2758. pathSegments.push(pArr[0], pArr[2]);
  2759. }
  2760. if (frame.right.visible) {
  2761. pathSegments.push(pArr[1], pArr[3]);
  2762. }
  2763. if (frame.top.visible) {
  2764. pathSegments.push(pArr[0], pArr[1]);
  2765. }
  2766. if (frame.bottom.visible) {
  2767. pathSegments.push(pArr[2], pArr[3]);
  2768. }
  2769. }
  2770. else { // X-Axis
  2771. if (frame.front.visible) {
  2772. pathSegments.push(pArr[0], pArr[2]);
  2773. }
  2774. if (frame.back.visible) {
  2775. pathSegments.push(pArr[1], pArr[3]);
  2776. }
  2777. if (frame.top.visible) {
  2778. pathSegments.push(pArr[0], pArr[1]);
  2779. }
  2780. if (frame.bottom.visible) {
  2781. pathSegments.push(pArr[2], pArr[3]);
  2782. }
  2783. }
  2784. pathSegments = perspective(pathSegments, this.chart, false);
  2785. return this.chart.renderer.toLineSegments(pathSegments);
  2786. });
  2787. // Do not draw axislines in 3D
  2788. wrap(Axis.prototype, 'getLinePath', function (proceed) {
  2789. // Do not do this if the chart is not 3D
  2790. if (!this.chart.is3d() || this.coll === 'colorAxis') {
  2791. return proceed.apply(this, [].slice.call(arguments, 1));
  2792. }
  2793. return [];
  2794. });
  2795. wrap(Axis.prototype, 'getPlotBandPath', function (proceed) {
  2796. // Do not do this if the chart is not 3D
  2797. if (!this.chart.is3d() || this.coll === 'colorAxis') {
  2798. return proceed.apply(this, [].slice.call(arguments, 1));
  2799. }
  2800. var args = arguments, from = args[1], to = args[2], path = [], fromPath = this.getPlotLinePath({ value: from }), toPath = this.getPlotLinePath({ value: to });
  2801. if (fromPath && toPath) {
  2802. for (var i = 0; i < fromPath.length; i += 6) {
  2803. path.push('M', fromPath[i + 1], fromPath[i + 2], 'L', fromPath[i + 4], fromPath[i + 5], 'L', toPath[i + 4], toPath[i + 5], 'L', toPath[i + 1], toPath[i + 2], 'Z');
  2804. }
  2805. }
  2806. return path;
  2807. });
  2808. /**
  2809. * @private
  2810. * @param {Highcharts.Axis} axis
  2811. * Related axis
  2812. * @param {Highcharts.Position3dObject} pos
  2813. * Position to fix
  2814. * @param {boolean} [isTitle]
  2815. * Whether this is a title position
  2816. * @return {Highcharts.Position3dObject}
  2817. * Fixed position
  2818. */
  2819. function fix3dPosition(axis, pos, isTitle) {
  2820. // Do not do this if the chart is not 3D
  2821. if (!axis.chart.is3d() || axis.coll === 'colorAxis') {
  2822. return pos;
  2823. }
  2824. var chart = axis.chart, alpha = deg2rad * chart.options.chart.options3d.alpha, beta = deg2rad * chart.options.chart.options3d.beta, positionMode = pick(isTitle && axis.options.title.position3d, axis.options.labels.position3d), skew = pick(isTitle && axis.options.title.skew3d, axis.options.labels.skew3d), frame = chart.frame3d, plotLeft = chart.plotLeft, plotRight = chart.plotWidth + plotLeft, plotTop = chart.plotTop, plotBottom = chart.plotHeight + plotTop,
  2825. // Indicates we are labelling an X or Z axis on the "back" of the chart
  2826. reverseFlap = false, offsetX = 0, offsetY = 0, vecX, vecY = { x: 0, y: 1, z: 0 };
  2827. pos = axis.swapZ({ x: pos.x, y: pos.y, z: 0 });
  2828. if (axis.isZAxis) { // Z Axis
  2829. if (axis.opposite) {
  2830. if (frame.axes.z.top === null) {
  2831. return {};
  2832. }
  2833. offsetY = pos.y - plotTop;
  2834. pos.x = frame.axes.z.top.x;
  2835. pos.y = frame.axes.z.top.y;
  2836. vecX = frame.axes.z.top.xDir;
  2837. reverseFlap = !frame.top.frontFacing;
  2838. }
  2839. else {
  2840. if (frame.axes.z.bottom === null) {
  2841. return {};
  2842. }
  2843. offsetY = pos.y - plotBottom;
  2844. pos.x = frame.axes.z.bottom.x;
  2845. pos.y = frame.axes.z.bottom.y;
  2846. vecX = frame.axes.z.bottom.xDir;
  2847. reverseFlap = !frame.bottom.frontFacing;
  2848. }
  2849. }
  2850. else if (axis.horiz) { // X Axis
  2851. if (axis.opposite) {
  2852. if (frame.axes.x.top === null) {
  2853. return {};
  2854. }
  2855. offsetY = pos.y - plotTop;
  2856. pos.y = frame.axes.x.top.y;
  2857. pos.z = frame.axes.x.top.z;
  2858. vecX = frame.axes.x.top.xDir;
  2859. reverseFlap = !frame.top.frontFacing;
  2860. }
  2861. else {
  2862. if (frame.axes.x.bottom === null) {
  2863. return {};
  2864. }
  2865. offsetY = pos.y - plotBottom;
  2866. pos.y = frame.axes.x.bottom.y;
  2867. pos.z = frame.axes.x.bottom.z;
  2868. vecX = frame.axes.x.bottom.xDir;
  2869. reverseFlap = !frame.bottom.frontFacing;
  2870. }
  2871. }
  2872. else { // Y Axis
  2873. if (axis.opposite) {
  2874. if (frame.axes.y.right === null) {
  2875. return {};
  2876. }
  2877. offsetX = pos.x - plotRight;
  2878. pos.x = frame.axes.y.right.x;
  2879. pos.z = frame.axes.y.right.z;
  2880. vecX = frame.axes.y.right.xDir;
  2881. // Rotate 90º on opposite edge
  2882. vecX = { x: vecX.z, y: vecX.y, z: -vecX.x };
  2883. }
  2884. else {
  2885. if (frame.axes.y.left === null) {
  2886. return {};
  2887. }
  2888. offsetX = pos.x - plotLeft;
  2889. pos.x = frame.axes.y.left.x;
  2890. pos.z = frame.axes.y.left.z;
  2891. vecX = frame.axes.y.left.xDir;
  2892. }
  2893. }
  2894. if (positionMode === 'chart') {
  2895. // Labels preserve their direction relative to the chart
  2896. // nothing to do
  2897. }
  2898. else if (positionMode === 'flap') {
  2899. // Labels are be rotated around the axis direction to face the screen
  2900. if (!axis.horiz) { // Y Axis
  2901. vecX = { x: Math.cos(beta), y: 0, z: Math.sin(beta) };
  2902. }
  2903. else { // X and Z Axis
  2904. var sin = Math.sin(alpha);
  2905. var cos = Math.cos(alpha);
  2906. if (axis.opposite) {
  2907. sin = -sin;
  2908. }
  2909. if (reverseFlap) {
  2910. sin = -sin;
  2911. }
  2912. vecY = { x: vecX.z * sin, y: cos, z: -vecX.x * sin };
  2913. }
  2914. }
  2915. else if (positionMode === 'ortho') {
  2916. // Labels will be rotated to be ortogonal to the axis
  2917. if (!axis.horiz) { // Y Axis
  2918. vecX = { x: Math.cos(beta), y: 0, z: Math.sin(beta) };
  2919. }
  2920. else { // X and Z Axis
  2921. var sina = Math.sin(alpha);
  2922. var cosa = Math.cos(alpha);
  2923. var sinb = Math.sin(beta);
  2924. var cosb = Math.cos(beta);
  2925. var vecZ = { x: sinb * cosa, y: -sina, z: -cosa * cosb };
  2926. vecY = {
  2927. x: vecX.y * vecZ.z - vecX.z * vecZ.y,
  2928. y: vecX.z * vecZ.x - vecX.x * vecZ.z,
  2929. z: vecX.x * vecZ.y - vecX.y * vecZ.x
  2930. };
  2931. var scale = 1 / Math.sqrt(vecY.x * vecY.x + vecY.y * vecY.y + vecY.z * vecY.z);
  2932. if (reverseFlap) {
  2933. scale = -scale;
  2934. }
  2935. vecY = { x: scale * vecY.x, y: scale * vecY.y, z: scale * vecY.z };
  2936. }
  2937. }
  2938. else { // positionMode == 'offset'
  2939. // Labels will be skewd to maintain vertical / horizontal offsets from
  2940. // axis
  2941. if (!axis.horiz) { // Y Axis
  2942. vecX = { x: Math.cos(beta), y: 0, z: Math.sin(beta) };
  2943. }
  2944. else { // X and Z Axis
  2945. vecY = {
  2946. x: Math.sin(beta) * Math.sin(alpha),
  2947. y: Math.cos(alpha),
  2948. z: -Math.cos(beta) * Math.sin(alpha)
  2949. };
  2950. }
  2951. }
  2952. pos.x += offsetX * vecX.x + offsetY * vecY.x;
  2953. pos.y += offsetX * vecX.y + offsetY * vecY.y;
  2954. pos.z += offsetX * vecX.z + offsetY * vecY.z;
  2955. var projected = perspective([pos], axis.chart)[0];
  2956. if (skew) {
  2957. // Check if the label text would be mirrored
  2958. var isMirrored = shapeArea(perspective([
  2959. pos,
  2960. { x: pos.x + vecX.x, y: pos.y + vecX.y, z: pos.z + vecX.z },
  2961. { x: pos.x + vecY.x, y: pos.y + vecY.y, z: pos.z + vecY.z }
  2962. ], axis.chart)) < 0;
  2963. if (isMirrored) {
  2964. vecX = { x: -vecX.x, y: -vecX.y, z: -vecX.z };
  2965. }
  2966. var pointsProjected = perspective([
  2967. { x: pos.x, y: pos.y, z: pos.z },
  2968. { x: pos.x + vecX.x, y: pos.y + vecX.y, z: pos.z + vecX.z },
  2969. { x: pos.x + vecY.x, y: pos.y + vecY.y, z: pos.z + vecY.z }
  2970. ], axis.chart);
  2971. projected.matrix = [
  2972. pointsProjected[1].x - pointsProjected[0].x,
  2973. pointsProjected[1].y - pointsProjected[0].y,
  2974. pointsProjected[2].x - pointsProjected[0].x,
  2975. pointsProjected[2].y - pointsProjected[0].y,
  2976. projected.x,
  2977. projected.y
  2978. ];
  2979. projected.matrix[4] -= projected.x * projected.matrix[0] +
  2980. projected.y * projected.matrix[2];
  2981. projected.matrix[5] -= projected.x * projected.matrix[1] +
  2982. projected.y * projected.matrix[3];
  2983. }
  2984. return projected;
  2985. }
  2986. /*
  2987. Tick extensions
  2988. */
  2989. wrap(Tick.prototype, 'getMarkPath', function (proceed) {
  2990. var path = proceed.apply(this, [].slice.call(arguments, 1));
  2991. var pArr = [
  2992. fix3dPosition(this.axis, { x: path[1], y: path[2], z: 0 }),
  2993. fix3dPosition(this.axis, { x: path[4], y: path[5], z: 0 })
  2994. ];
  2995. return this.axis.chart.renderer.toLineSegments(pArr);
  2996. });
  2997. addEvent(Tick, 'afterGetLabelPosition', function (e) {
  2998. extend(e.pos, fix3dPosition(this.axis, e.pos));
  2999. });
  3000. wrap(Axis.prototype, 'getTitlePosition', function (proceed) {
  3001. var pos = proceed.apply(this, [].slice.call(arguments, 1));
  3002. return fix3dPosition(this, pos, true);
  3003. });
  3004. addEvent(Axis, 'drawCrosshair', function (e) {
  3005. if (this.chart.is3d() && this.coll !== 'colorAxis') {
  3006. if (e.point) {
  3007. e.point.crosshairPos = this.isXAxis ?
  3008. e.point.axisXpos :
  3009. this.len - e.point.axisYpos;
  3010. }
  3011. }
  3012. });
  3013. addEvent(Axis, 'destroy', function () {
  3014. ['backFrame', 'bottomFrame', 'sideFrame'].forEach(function (prop) {
  3015. if (this[prop]) {
  3016. this[prop] = this[prop].destroy();
  3017. }
  3018. }, this);
  3019. });
  3020. /*
  3021. Z-AXIS
  3022. */
  3023. Chart.prototype.addZAxis = function (options) {
  3024. return new ZAxis(this, options);
  3025. };
  3026. Chart.prototype.collectionsWithUpdate.push('zAxis');
  3027. Chart.prototype.collectionsWithInit.zAxis = [Chart.prototype.addZAxis];
  3028. Axis.prototype.swapZ = function (p, insidePlotArea) {
  3029. if (this.isZAxis) {
  3030. var plotLeft = insidePlotArea ? 0 : this.chart.plotLeft;
  3031. return {
  3032. x: plotLeft + p.z,
  3033. y: p.y,
  3034. z: p.x - plotLeft
  3035. };
  3036. }
  3037. return p;
  3038. };
  3039. ZAxis = H.ZAxis = function () {
  3040. this.init.apply(this, arguments);
  3041. };
  3042. extend(ZAxis.prototype, Axis.prototype);
  3043. extend(ZAxis.prototype, {
  3044. isZAxis: true,
  3045. setOptions: function (userOptions) {
  3046. userOptions = merge({
  3047. offset: 0,
  3048. lineWidth: 0
  3049. }, userOptions);
  3050. Axis.prototype.setOptions.call(this, userOptions);
  3051. this.coll = 'zAxis';
  3052. },
  3053. setAxisSize: function () {
  3054. Axis.prototype.setAxisSize.call(this);
  3055. this.width = this.len =
  3056. this.chart.options.chart.options3d.depth;
  3057. this.right = this.chart.chartWidth - this.width - this.left;
  3058. },
  3059. getSeriesExtremes: function () {
  3060. var axis = this, chart = axis.chart;
  3061. axis.hasVisibleSeries = false;
  3062. // Reset properties in case we're redrawing (#3353)
  3063. axis.dataMin =
  3064. axis.dataMax =
  3065. axis.ignoreMinPadding =
  3066. axis.ignoreMaxPadding = null;
  3067. if (axis.buildStacks) {
  3068. axis.buildStacks();
  3069. }
  3070. // loop through this axis' series
  3071. axis.series.forEach(function (series) {
  3072. if (series.visible ||
  3073. !chart.options.chart.ignoreHiddenSeries) {
  3074. var seriesOptions = series.options, zData, threshold = seriesOptions.threshold;
  3075. axis.hasVisibleSeries = true;
  3076. // Validate threshold in logarithmic axes
  3077. if (axis.positiveValuesOnly && threshold <= 0) {
  3078. threshold = null;
  3079. }
  3080. zData = series.zData;
  3081. if (zData.length) {
  3082. axis.dataMin = Math.min(pick(axis.dataMin, zData[0]), Math.min.apply(null, zData));
  3083. axis.dataMax = Math.max(pick(axis.dataMax, zData[0]), Math.max.apply(null, zData));
  3084. }
  3085. }
  3086. });
  3087. }
  3088. });
  3089. // Get the Z axis in addition to the default X and Y.
  3090. addEvent(Chart, 'afterGetAxes', function () {
  3091. var chart = this, options = this.options, zAxisOptions = options.zAxis = splat(options.zAxis || {});
  3092. if (!chart.is3d()) {
  3093. return;
  3094. }
  3095. this.zAxis = [];
  3096. zAxisOptions.forEach(function (axisOptions, i) {
  3097. axisOptions.index = i;
  3098. // Z-Axis is shown horizontally, so it's kind of a X-Axis
  3099. axisOptions.isX = true;
  3100. var zAxis = chart.addZAxis(axisOptions);
  3101. zAxis.setScale();
  3102. });
  3103. });
  3104. // Wrap getSlotWidth function to calculate individual width value for each slot
  3105. // (#8042).
  3106. wrap(Axis.prototype, 'getSlotWidth', function (proceed, tick) {
  3107. if (this.chart.is3d() &&
  3108. tick &&
  3109. tick.label &&
  3110. this.categories &&
  3111. this.chart.frameShapes) {
  3112. var chart = this.chart, ticks = this.ticks, gridGroup = this.gridGroup.element.childNodes, firstGridLine = gridGroup[0].getBBox(), frame3DLeft = chart.frameShapes.left.getBBox(), options3d = chart.options.chart.options3d, origin = {
  3113. x: chart.plotWidth / 2,
  3114. y: chart.plotHeight / 2,
  3115. z: options3d.depth / 2,
  3116. vd: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0)
  3117. }, labelPos, prevLabelPos, nextLabelPos, slotWidth, tickId = tick.pos, prevTick = ticks[tickId - 1], nextTick = ticks[tickId + 1];
  3118. // Check whether the tick is not the first one and previous tick exists,
  3119. // then calculate position of previous label.
  3120. if (tickId !== 0 && prevTick && prevTick.label.xy) { // #8621
  3121. prevLabelPos = perspective3D({
  3122. x: prevTick.label.xy.x,
  3123. y: prevTick.label.xy.y,
  3124. z: null
  3125. }, origin, origin.vd);
  3126. }
  3127. // If next label position is defined, then recalculate its position
  3128. // basing on the perspective.
  3129. if (nextTick && nextTick.label.xy) {
  3130. nextLabelPos = perspective3D({
  3131. x: nextTick.label.xy.x,
  3132. y: nextTick.label.xy.y,
  3133. z: null
  3134. }, origin, origin.vd);
  3135. }
  3136. labelPos = {
  3137. x: tick.label.xy.x,
  3138. y: tick.label.xy.y,
  3139. z: null
  3140. };
  3141. labelPos = perspective3D(labelPos, origin, origin.vd);
  3142. // If tick is first one, check whether next label position is already
  3143. // calculated, then return difference between the first and the second
  3144. // label. If there is no next label position calculated, return the
  3145. // difference between the first grid line and left 3d frame.
  3146. slotWidth = Math.abs(prevLabelPos ?
  3147. labelPos.x - prevLabelPos.x : nextLabelPos ?
  3148. nextLabelPos.x - labelPos.x :
  3149. firstGridLine.x - frame3DLeft.x);
  3150. return slotWidth;
  3151. }
  3152. return proceed.apply(this, [].slice.call(arguments, 1));
  3153. });
  3154. });
  3155. _registerModule(_modules, 'parts-3d/Series.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  3156. /* *
  3157. *
  3158. * (c) 2010-2019 Torstein Honsi
  3159. *
  3160. * Extension to the Series object in 3D charts.
  3161. *
  3162. * License: www.highcharts.com/license
  3163. *
  3164. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3165. *
  3166. * */
  3167. var pick = U.pick;
  3168. var addEvent = H.addEvent, perspective = H.perspective;
  3169. /* eslint-disable no-invalid-this */
  3170. // Wrap the translate method to post-translate points into 3D perspective
  3171. addEvent(H.Series, 'afterTranslate', function () {
  3172. if (this.chart.is3d()) {
  3173. this.translate3dPoints();
  3174. }
  3175. });
  3176. // Translate the plotX, plotY properties and add plotZ.
  3177. H.Series.prototype.translate3dPoints = function () {
  3178. var series = this, chart = series.chart, zAxis = pick(series.zAxis, chart.options.zAxis[0]), rawPoints = [], rawPoint, projectedPoints, projectedPoint, zValue, i;
  3179. for (i = 0; i < series.data.length; i++) {
  3180. rawPoint = series.data[i];
  3181. if (zAxis && zAxis.translate) {
  3182. zValue = zAxis.isLog && zAxis.val2lin ?
  3183. zAxis.val2lin(rawPoint.z) :
  3184. rawPoint.z; // #4562
  3185. rawPoint.plotZ = zAxis.translate(zValue);
  3186. rawPoint.isInside = rawPoint.isInside ?
  3187. (zValue >= zAxis.min &&
  3188. zValue <= zAxis.max) :
  3189. false;
  3190. }
  3191. else {
  3192. rawPoint.plotZ = 0;
  3193. }
  3194. rawPoint.axisXpos = rawPoint.plotX;
  3195. rawPoint.axisYpos = rawPoint.plotY;
  3196. rawPoint.axisZpos = rawPoint.plotZ;
  3197. rawPoints.push({
  3198. x: rawPoint.plotX,
  3199. y: rawPoint.plotY,
  3200. z: rawPoint.plotZ
  3201. });
  3202. }
  3203. projectedPoints = perspective(rawPoints, chart, true);
  3204. for (i = 0; i < series.data.length; i++) {
  3205. rawPoint = series.data[i];
  3206. projectedPoint = projectedPoints[i];
  3207. rawPoint.plotX = projectedPoint.x;
  3208. rawPoint.plotY = projectedPoint.y;
  3209. rawPoint.plotZ = projectedPoint.z;
  3210. }
  3211. };
  3212. });
  3213. _registerModule(_modules, 'parts-3d/Column.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  3214. /* *
  3215. *
  3216. * (c) 2010-2019 Torstein Honsi
  3217. *
  3218. * License: www.highcharts.com/license
  3219. *
  3220. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3221. *
  3222. * */
  3223. var pick = U.pick, wrap = U.wrap;
  3224. var addEvent = H.addEvent, perspective = H.perspective, Series = H.Series, seriesTypes = H.seriesTypes, svg = H.svg;
  3225. /**
  3226. * Depth of the columns in a 3D column chart.
  3227. *
  3228. * @type {number}
  3229. * @default 25
  3230. * @since 4.0
  3231. * @product highcharts
  3232. * @requires highcharts-3d
  3233. * @apioption plotOptions.column.depth
  3234. */
  3235. /**
  3236. * 3D columns only. The color of the edges. Similar to `borderColor`, except it
  3237. * defaults to the same color as the column.
  3238. *
  3239. * @type {Highcharts.ColorString}
  3240. * @product highcharts
  3241. * @requires highcharts-3d
  3242. * @apioption plotOptions.column.edgeColor
  3243. */
  3244. /**
  3245. * 3D columns only. The width of the colored edges.
  3246. *
  3247. * @type {number}
  3248. * @default 1
  3249. * @product highcharts
  3250. * @requires highcharts-3d
  3251. * @apioption plotOptions.column.edgeWidth
  3252. */
  3253. /**
  3254. * The spacing between columns on the Z Axis in a 3D chart.
  3255. *
  3256. * @type {number}
  3257. * @default 1
  3258. * @since 4.0
  3259. * @product highcharts
  3260. * @requires highcharts-3d
  3261. * @apioption plotOptions.column.groupZPadding
  3262. */
  3263. /* eslint-disable no-invalid-this */
  3264. wrap(seriesTypes.column.prototype, 'translate', function (proceed) {
  3265. proceed.apply(this, [].slice.call(arguments, 1));
  3266. // Do not do this if the chart is not 3D
  3267. if (this.chart.is3d()) {
  3268. this.translate3dShapes();
  3269. }
  3270. });
  3271. // In 3D we need to pass point.outsidePlot option to the justifyDataLabel
  3272. // method for disabling justifying dataLabels in columns outside plot
  3273. wrap(H.Series.prototype, 'alignDataLabel', function (proceed) {
  3274. arguments[3].outside3dPlot = arguments[1].outside3dPlot;
  3275. proceed.apply(this, [].slice.call(arguments, 1));
  3276. });
  3277. // Don't use justifyDataLabel when point is outsidePlot
  3278. wrap(H.Series.prototype, 'justifyDataLabel', function (proceed) {
  3279. return !(arguments[2].outside3dPlot) ?
  3280. proceed.apply(this, [].slice.call(arguments, 1)) :
  3281. false;
  3282. });
  3283. seriesTypes.column.prototype.translate3dPoints = function () { };
  3284. seriesTypes.column.prototype.translate3dShapes = function () {
  3285. var series = this, chart = series.chart, seriesOptions = series.options, depth = seriesOptions.depth || 25, stack = seriesOptions.stacking ?
  3286. (seriesOptions.stack || 0) :
  3287. series.index, // #4743
  3288. z = stack * (depth + (seriesOptions.groupZPadding || 1)), borderCrisp = series.borderWidth % 2 ? 0.5 : 0;
  3289. if (chart.inverted && !series.yAxis.reversed) {
  3290. borderCrisp *= -1;
  3291. }
  3292. if (seriesOptions.grouping !== false) {
  3293. z = 0;
  3294. }
  3295. z += (seriesOptions.groupZPadding || 1);
  3296. series.data.forEach(function (point) {
  3297. // #7103 Reset outside3dPlot flag
  3298. point.outside3dPlot = null;
  3299. if (point.y !== null) {
  3300. var shapeArgs = point.shapeArgs, tooltipPos = point.tooltipPos,
  3301. // Array for final shapeArgs calculation.
  3302. // We are checking two dimensions (x and y).
  3303. dimensions = [['x', 'width'], ['y', 'height']], borderlessBase; // Crisped rects can have +/- 0.5 pixels offset.
  3304. // #3131 We need to check if column is inside plotArea.
  3305. dimensions.forEach(function (d) {
  3306. borderlessBase = shapeArgs[d[0]] - borderCrisp;
  3307. if (borderlessBase < 0) {
  3308. // If borderLessBase is smaller than 0, it is needed to set
  3309. // its value to 0 or 0.5 depending on borderWidth
  3310. // borderWidth may be even or odd.
  3311. shapeArgs[d[1]] +=
  3312. shapeArgs[d[0]] + borderCrisp;
  3313. shapeArgs[d[0]] = -borderCrisp;
  3314. borderlessBase = 0;
  3315. }
  3316. if ((borderlessBase + shapeArgs[d[1]] >
  3317. series[d[0] + 'Axis'].len) &&
  3318. // Do not change height/width of column if 0 (#6708)
  3319. shapeArgs[d[1]] !== 0) {
  3320. shapeArgs[d[1]] =
  3321. series[d[0] + 'Axis'].len -
  3322. shapeArgs[d[0]];
  3323. }
  3324. if (
  3325. // Do not remove columns with zero height/width.
  3326. (shapeArgs[d[1]] !== 0) &&
  3327. (shapeArgs[d[0]] >=
  3328. series[d[0] + 'Axis'].len ||
  3329. shapeArgs[d[0]] + shapeArgs[d[1]] <=
  3330. borderCrisp)) {
  3331. // Set args to 0 if column is outside the chart.
  3332. for (var key in shapeArgs) { // eslint-disable-line guard-for-in
  3333. shapeArgs[key] = 0;
  3334. }
  3335. // #7103 outside3dPlot flag is set on Points which are
  3336. // currently outside of plot.
  3337. point.outside3dPlot = true;
  3338. }
  3339. });
  3340. // Change from 2d to 3d
  3341. if (point.shapeType === 'rect') {
  3342. point.shapeType = 'cuboid';
  3343. }
  3344. shapeArgs.z = z;
  3345. shapeArgs.depth = depth;
  3346. shapeArgs.insidePlotArea = true;
  3347. // Translate the tooltip position in 3d space
  3348. tooltipPos = perspective([{
  3349. x: tooltipPos[0],
  3350. y: tooltipPos[1],
  3351. z: z
  3352. }], chart, true)[0];
  3353. point.tooltipPos = [tooltipPos.x, tooltipPos.y];
  3354. }
  3355. });
  3356. // store for later use #4067
  3357. series.z = z;
  3358. };
  3359. wrap(seriesTypes.column.prototype, 'animate', function (proceed) {
  3360. if (!this.chart.is3d()) {
  3361. proceed.apply(this, [].slice.call(arguments, 1));
  3362. }
  3363. else {
  3364. var args = arguments, init = args[1], yAxis = this.yAxis, series = this, reversed = this.yAxis.reversed;
  3365. if (svg) { // VML is too slow anyway
  3366. if (init) {
  3367. series.data.forEach(function (point) {
  3368. if (point.y !== null) {
  3369. point.height = point.shapeArgs.height;
  3370. point.shapey = point.shapeArgs.y; // #2968
  3371. point.shapeArgs.height = 1;
  3372. if (!reversed) {
  3373. if (point.stackY) {
  3374. point.shapeArgs.y =
  3375. point.plotY +
  3376. yAxis.translate(point.stackY);
  3377. }
  3378. else {
  3379. point.shapeArgs.y =
  3380. point.plotY +
  3381. (point.negative ?
  3382. -point.height :
  3383. point.height);
  3384. }
  3385. }
  3386. }
  3387. });
  3388. }
  3389. else { // run the animation
  3390. series.data.forEach(function (point) {
  3391. if (point.y !== null) {
  3392. point.shapeArgs.height = point.height;
  3393. point.shapeArgs.y = point.shapey; // #2968
  3394. // null value do not have a graphic
  3395. if (point.graphic) {
  3396. point.graphic.animate(point.shapeArgs, series.options.animation);
  3397. }
  3398. }
  3399. });
  3400. // redraw datalabels to the correct position
  3401. this.drawDataLabels();
  3402. // delete this function to allow it only once
  3403. series.animate = null;
  3404. }
  3405. }
  3406. }
  3407. });
  3408. // In case of 3d columns there is no sense to add this columns to a specific
  3409. // series group - if series is added to a group all columns will have the same
  3410. // zIndex in comparison with different series.
  3411. wrap(seriesTypes.column.prototype, 'plotGroup', function (proceed, prop, name, visibility, zIndex, parent) {
  3412. if (prop !== 'dataLabelsGroup') {
  3413. if (this.chart.is3d()) {
  3414. if (this[prop]) {
  3415. delete this[prop];
  3416. }
  3417. if (parent) {
  3418. if (!this.chart.columnGroup) {
  3419. this.chart.columnGroup =
  3420. this.chart.renderer.g('columnGroup').add(parent);
  3421. }
  3422. this[prop] = this.chart.columnGroup;
  3423. this.chart.columnGroup.attr(this.getPlotBox());
  3424. this[prop].survive = true;
  3425. if (prop === 'group' || prop === 'markerGroup') {
  3426. arguments[3] = 'visible';
  3427. // For 3D column group and markerGroup should be visible
  3428. }
  3429. }
  3430. }
  3431. }
  3432. return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
  3433. });
  3434. // When series is not added to group it is needed to change setVisible method to
  3435. // allow correct Legend funcionality. This wrap is basing on pie chart series.
  3436. wrap(seriesTypes.column.prototype, 'setVisible', function (proceed, vis) {
  3437. var series = this, pointVis;
  3438. if (series.chart.is3d()) {
  3439. series.data.forEach(function (point) {
  3440. point.visible = point.options.visible = vis =
  3441. typeof vis === 'undefined' ?
  3442. !pick(series.visible, point.visible) : vis;
  3443. pointVis = vis ? 'visible' : 'hidden';
  3444. series.options.data[series.data.indexOf(point)] =
  3445. point.options;
  3446. if (point.graphic) {
  3447. point.graphic.attr({
  3448. visibility: pointVis
  3449. });
  3450. }
  3451. });
  3452. }
  3453. proceed.apply(this, Array.prototype.slice.call(arguments, 1));
  3454. });
  3455. seriesTypes.column.prototype
  3456. .handle3dGrouping = true;
  3457. addEvent(Series, 'afterInit', function () {
  3458. if (this.chart.is3d() &&
  3459. this.handle3dGrouping) {
  3460. var seriesOptions = this.options, grouping = seriesOptions.grouping, stacking = seriesOptions.stacking, reversedStacks = pick(this.yAxis.options.reversedStacks, true), z = 0;
  3461. // @todo grouping === true ?
  3462. if (!(typeof grouping !== 'undefined' && !grouping)) {
  3463. var stacks = this.chart.retrieveStacks(stacking), stack = seriesOptions.stack || 0, i; // position within the stack
  3464. for (i = 0; i < stacks[stack].series.length; i++) {
  3465. if (stacks[stack].series[i] === this) {
  3466. break;
  3467. }
  3468. }
  3469. z = (10 * (stacks.totalStacks - stacks[stack].position)) +
  3470. (reversedStacks ? i : -i); // #4369
  3471. // In case when axis is reversed, columns are also reversed inside
  3472. // the group (#3737)
  3473. if (!this.xAxis.reversed) {
  3474. z = (stacks.totalStacks * 10) - z;
  3475. }
  3476. }
  3477. seriesOptions.zIndex = z;
  3478. }
  3479. });
  3480. // eslint-disable-next-line valid-jsdoc
  3481. /**
  3482. * @private
  3483. */
  3484. function pointAttribs(proceed) {
  3485. var attr = proceed.apply(this, [].slice.call(arguments, 1));
  3486. if (this.chart.is3d && this.chart.is3d()) {
  3487. // Set the fill color to the fill color to provide a smooth edge
  3488. attr.stroke = this.options.edgeColor || attr.fill;
  3489. attr['stroke-width'] = pick(this.options.edgeWidth, 1); // #4055
  3490. }
  3491. return attr;
  3492. }
  3493. // eslint-disable-next-line valid-jsdoc
  3494. /**
  3495. * In 3D mode, all column-series are rendered in one main group. Because of that
  3496. * we need to apply inactive state on all points.
  3497. * @private
  3498. */
  3499. function setState(proceed, state, inherit) {
  3500. var is3d = this.chart.is3d && this.chart.is3d();
  3501. if (is3d) {
  3502. this.options.inactiveOtherPoints = true;
  3503. }
  3504. proceed.call(this, state, inherit);
  3505. if (is3d) {
  3506. this.options.inactiveOtherPoints = false;
  3507. }
  3508. }
  3509. // eslint-disable-next-line valid-jsdoc
  3510. /**
  3511. * In 3D mode, simple checking for a new shape to animate is not enough.
  3512. * Additionally check if graphic is a group of elements
  3513. * @private
  3514. */
  3515. function hasNewShapeType(proceed) {
  3516. var args = [];
  3517. for (var _i = 1; _i < arguments.length; _i++) {
  3518. args[_i - 1] = arguments[_i];
  3519. }
  3520. return this.series.chart.is3d() ?
  3521. this.graphic && this.graphic.element.nodeName !== 'g' :
  3522. proceed.apply(this, args);
  3523. }
  3524. wrap(seriesTypes.column.prototype, 'pointAttribs', pointAttribs);
  3525. wrap(seriesTypes.column.prototype, 'setState', setState);
  3526. wrap(seriesTypes.column.prototype.pointClass.prototype, 'hasNewShapeType', hasNewShapeType);
  3527. if (seriesTypes.columnrange) {
  3528. wrap(seriesTypes.columnrange.prototype, 'pointAttribs', pointAttribs);
  3529. wrap(seriesTypes.columnrange.prototype, 'setState', setState);
  3530. wrap(seriesTypes.columnrange.prototype.pointClass.prototype, 'hasNewShapeType', hasNewShapeType);
  3531. seriesTypes.columnrange.prototype.plotGroup =
  3532. seriesTypes.column.prototype.plotGroup;
  3533. seriesTypes.columnrange.prototype.setVisible =
  3534. seriesTypes.column.prototype.setVisible;
  3535. }
  3536. wrap(Series.prototype, 'alignDataLabel', function (proceed) {
  3537. // Only do this for 3D columns and it's derived series
  3538. if (this.chart.is3d() &&
  3539. this instanceof seriesTypes.column) {
  3540. var series = this, chart = series.chart;
  3541. var args = arguments, alignTo = args[4], point = args[1];
  3542. var pos = ({ x: alignTo.x, y: alignTo.y, z: series.z });
  3543. pos = perspective([pos], chart, true)[0];
  3544. alignTo.x = pos.x;
  3545. // #7103 If point is outside of plotArea, hide data label.
  3546. alignTo.y = point.outside3dPlot ? -9e9 : pos.y;
  3547. }
  3548. proceed.apply(this, [].slice.call(arguments, 1));
  3549. });
  3550. // Added stackLabels position calculation for 3D charts.
  3551. wrap(H.StackItem.prototype, 'getStackBox', function (proceed, chart) {
  3552. var stackBox = proceed.apply(this, [].slice.call(arguments, 1));
  3553. // Only do this for 3D chart.
  3554. if (chart.is3d()) {
  3555. var pos = ({
  3556. x: stackBox.x,
  3557. y: stackBox.y,
  3558. z: 0
  3559. });
  3560. pos = H.perspective([pos], chart, true)[0];
  3561. stackBox.x = pos.x;
  3562. stackBox.y = pos.y;
  3563. }
  3564. return stackBox;
  3565. });
  3566. });
  3567. _registerModule(_modules, 'parts-3d/Pie.js', [_modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (H, U) {
  3568. /* *
  3569. *
  3570. * (c) 2010-2019 Torstein Honsi
  3571. *
  3572. * 3D pie series
  3573. *
  3574. * License: www.highcharts.com/license
  3575. *
  3576. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3577. *
  3578. * */
  3579. var pick = U.pick, wrap = U.wrap;
  3580. var deg2rad = H.deg2rad, seriesTypes = H.seriesTypes, svg = H.svg;
  3581. /**
  3582. * The thickness of a 3D pie.
  3583. *
  3584. * @type {number}
  3585. * @default 0
  3586. * @since 4.0
  3587. * @product highcharts
  3588. * @requires highcharts-3d
  3589. * @apioption plotOptions.pie.depth
  3590. */
  3591. /* eslint-disable no-invalid-this */
  3592. wrap(seriesTypes.pie.prototype, 'translate', function (proceed) {
  3593. proceed.apply(this, [].slice.call(arguments, 1));
  3594. // Do not do this if the chart is not 3D
  3595. if (!this.chart.is3d()) {
  3596. return;
  3597. }
  3598. var series = this, seriesOptions = series.options, depth = seriesOptions.depth || 0, options3d = series.chart.options.chart.options3d, alpha = options3d.alpha, beta = options3d.beta, z = seriesOptions.stacking ?
  3599. (seriesOptions.stack || 0) * depth :
  3600. series._i * depth;
  3601. z += depth / 2;
  3602. if (seriesOptions.grouping !== false) {
  3603. z = 0;
  3604. }
  3605. series.data.forEach(function (point) {
  3606. var shapeArgs = point.shapeArgs, angle;
  3607. point.shapeType = 'arc3d';
  3608. shapeArgs.z = z;
  3609. shapeArgs.depth = depth * 0.75;
  3610. shapeArgs.alpha = alpha;
  3611. shapeArgs.beta = beta;
  3612. shapeArgs.center = series.center;
  3613. angle = (shapeArgs.end + shapeArgs.start) / 2;
  3614. point.slicedTranslation = {
  3615. translateX: Math.round(Math.cos(angle) *
  3616. seriesOptions.slicedOffset *
  3617. Math.cos(alpha * deg2rad)),
  3618. translateY: Math.round(Math.sin(angle) *
  3619. seriesOptions.slicedOffset *
  3620. Math.cos(alpha * deg2rad))
  3621. };
  3622. });
  3623. });
  3624. wrap(seriesTypes.pie.prototype.pointClass.prototype, 'haloPath', function (proceed) {
  3625. var args = arguments;
  3626. return this.series.chart.is3d() ? [] : proceed.call(this, args[1]);
  3627. });
  3628. wrap(seriesTypes.pie.prototype, 'pointAttribs', function (proceed, point, state) {
  3629. var attr = proceed.call(this, point, state), options = this.options;
  3630. if (this.chart.is3d() && !this.chart.styledMode) {
  3631. attr.stroke = options.edgeColor || point.color || this.color;
  3632. attr['stroke-width'] = pick(options.edgeWidth, 1);
  3633. }
  3634. return attr;
  3635. });
  3636. wrap(seriesTypes.pie.prototype, 'drawDataLabels', function (proceed) {
  3637. if (this.chart.is3d()) {
  3638. var series = this, chart = series.chart, options3d = chart.options.chart.options3d;
  3639. series.data.forEach(function (point) {
  3640. var shapeArgs = point.shapeArgs, r = shapeArgs.r,
  3641. // #3240 issue with datalabels for 0 and null values
  3642. a1 = (shapeArgs.alpha || options3d.alpha) * deg2rad, b1 = (shapeArgs.beta || options3d.beta) * deg2rad, a2 = (shapeArgs.start + shapeArgs.end) / 2, labelPosition = point.labelPosition, connectorPosition = labelPosition.connectorPosition, yOffset = (-r * (1 - Math.cos(a1)) * Math.sin(a2)), xOffset = r * (Math.cos(b1) - 1) * Math.cos(a2);
  3643. // Apply perspective on label positions
  3644. [
  3645. labelPosition.natural,
  3646. connectorPosition.breakAt,
  3647. connectorPosition.touchingSliceAt
  3648. ].forEach(function (coordinates) {
  3649. coordinates.x += xOffset;
  3650. coordinates.y += yOffset;
  3651. });
  3652. });
  3653. }
  3654. proceed.apply(this, [].slice.call(arguments, 1));
  3655. });
  3656. wrap(seriesTypes.pie.prototype, 'addPoint', function (proceed) {
  3657. proceed.apply(this, [].slice.call(arguments, 1));
  3658. if (this.chart.is3d()) {
  3659. // destroy (and rebuild) everything!!!
  3660. this.update(this.userOptions, true); // #3845 pass the old options
  3661. }
  3662. });
  3663. wrap(seriesTypes.pie.prototype, 'animate', function (proceed) {
  3664. if (!this.chart.is3d()) {
  3665. proceed.apply(this, [].slice.call(arguments, 1));
  3666. }
  3667. else {
  3668. var args = arguments, init = args[1], animation = this.options.animation, attribs, center = this.center, group = this.group, markerGroup = this.markerGroup;
  3669. if (svg) { // VML is too slow anyway
  3670. if (animation === true) {
  3671. animation = {};
  3672. }
  3673. // Initialize the animation
  3674. if (init) {
  3675. // Scale down the group and place it in the center
  3676. group.oldtranslateX = group.translateX;
  3677. group.oldtranslateY = group.translateY;
  3678. attribs = {
  3679. translateX: center[0],
  3680. translateY: center[1],
  3681. scaleX: 0.001,
  3682. scaleY: 0.001
  3683. };
  3684. group.attr(attribs);
  3685. if (markerGroup) {
  3686. markerGroup.attrSetters = group.attrSetters;
  3687. markerGroup.attr(attribs);
  3688. }
  3689. // Run the animation
  3690. }
  3691. else {
  3692. attribs = {
  3693. translateX: group.oldtranslateX,
  3694. translateY: group.oldtranslateY,
  3695. scaleX: 1,
  3696. scaleY: 1
  3697. };
  3698. group.animate(attribs, animation);
  3699. if (markerGroup) {
  3700. markerGroup.animate(attribs, animation);
  3701. }
  3702. // Delete this function to allow it only once
  3703. this.animate = null;
  3704. }
  3705. }
  3706. }
  3707. });
  3708. });
  3709. _registerModule(_modules, 'parts-3d/Scatter.js', [_modules['parts/Globals.js']], function (H) {
  3710. /* *
  3711. *
  3712. * (c) 2010-2019 Torstein Honsi
  3713. *
  3714. * Scatter 3D series.
  3715. *
  3716. * License: www.highcharts.com/license
  3717. *
  3718. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3719. *
  3720. * */
  3721. var Point = H.Point, seriesType = H.seriesType, seriesTypes = H.seriesTypes;
  3722. /**
  3723. * @private
  3724. * @class
  3725. * @name Highcharts.seriesTypes.scatter3d
  3726. *
  3727. * @augments Highcharts.Series
  3728. */
  3729. seriesType('scatter3d', 'scatter',
  3730. /**
  3731. * A 3D scatter plot uses x, y and z coordinates to display values for three
  3732. * variables for a set of data.
  3733. *
  3734. * @sample {highcharts} highcharts/3d/scatter/
  3735. * Simple 3D scatter
  3736. * @sample {highcharts} highcharts/demo/3d-scatter-draggable
  3737. * Draggable 3d scatter
  3738. *
  3739. * @extends plotOptions.scatter
  3740. * @excluding dragDrop, cluster
  3741. * @product highcharts
  3742. * @requires highcharts-3d
  3743. * @optionparent plotOptions.scatter3d
  3744. */
  3745. {
  3746. tooltip: {
  3747. pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>'
  3748. }
  3749. // Series class
  3750. }, {
  3751. pointAttribs: function (point) {
  3752. var attribs = seriesTypes.scatter.prototype.pointAttribs
  3753. .apply(this, arguments);
  3754. if (this.chart.is3d() && point) {
  3755. attribs.zIndex =
  3756. H.pointCameraDistance(point, this.chart);
  3757. }
  3758. return attribs;
  3759. },
  3760. axisTypes: ['xAxis', 'yAxis', 'zAxis'],
  3761. pointArrayMap: ['x', 'y', 'z'],
  3762. parallelArrays: ['x', 'y', 'z'],
  3763. // Require direct touch rather than using the k-d-tree, because the
  3764. // k-d-tree currently doesn't take the xyz coordinate system into
  3765. // account (#4552)
  3766. directTouch: true
  3767. // Point class
  3768. }, {
  3769. applyOptions: function () {
  3770. Point.prototype.applyOptions.apply(this, arguments);
  3771. if (typeof this.z === 'undefined') {
  3772. this.z = 0;
  3773. }
  3774. return this;
  3775. }
  3776. });
  3777. /**
  3778. * A `scatter3d` series. If the [type](#series.scatter3d.type) option is
  3779. * not specified, it is inherited from [chart.type](#chart.type).
  3780. *
  3781. * scatter3d](#plotOptions.scatter3d).
  3782. *
  3783. * @extends series,plotOptions.scatter3d
  3784. * @product highcharts
  3785. * @requires highcharts-3d
  3786. * @apioption series.scatter3d
  3787. */
  3788. /**
  3789. * An array of data points for the series. For the `scatter3d` series
  3790. * type, points can be given in the following ways:
  3791. *
  3792. * 1. An array of arrays with 3 values. In this case, the values correspond
  3793. * to `x,y,z`. If the first value is a string, it is applied as the name
  3794. * of the point, and the `x` value is inferred.
  3795. *
  3796. * ```js
  3797. * data: [
  3798. * [0, 0, 1],
  3799. * [1, 8, 7],
  3800. * [2, 9, 2]
  3801. * ]
  3802. * ```
  3803. *
  3804. * 3. An array of objects with named values. The following snippet shows only a
  3805. * few settings, see the complete options set below. If the total number of data
  3806. * points exceeds the series'
  3807. * [turboThreshold](#series.scatter3d.turboThreshold), this option is not
  3808. * available.
  3809. *
  3810. * ```js
  3811. * data: [{
  3812. * x: 1,
  3813. * y: 2,
  3814. * z: 24,
  3815. * name: "Point2",
  3816. * color: "#00FF00"
  3817. * }, {
  3818. * x: 1,
  3819. * y: 4,
  3820. * z: 12,
  3821. * name: "Point1",
  3822. * color: "#FF00FF"
  3823. * }]
  3824. * ```
  3825. *
  3826. * @sample {highcharts} highcharts/chart/reflow-true/
  3827. * Numerical values
  3828. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  3829. * Arrays of numeric x and y
  3830. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  3831. * Arrays of datetime x and y
  3832. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  3833. * Arrays of point.name and y
  3834. * @sample {highcharts} highcharts/series/data-array-of-objects/
  3835. * Config objects
  3836. *
  3837. * @type {Array<Array<number>|*>}
  3838. * @extends series.scatter.data
  3839. * @product highcharts
  3840. * @apioption series.scatter3d.data
  3841. */
  3842. /**
  3843. * The z value for each data point.
  3844. *
  3845. * @type {number}
  3846. * @product highcharts
  3847. * @apioption series.scatter3d.data.z
  3848. */
  3849. ''; // adds doclets above to transpiled file
  3850. });
  3851. _registerModule(_modules, 'parts-3d/VMLRenderer.js', [_modules['parts/Globals.js']], function (H) {
  3852. /* *
  3853. *
  3854. * (c) 2010-2019 Torstein Honsi
  3855. *
  3856. * Extension to the VML Renderer
  3857. *
  3858. * License: www.highcharts.com/license
  3859. *
  3860. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  3861. *
  3862. * */
  3863. var addEvent = H.addEvent, Axis = H.Axis, SVGRenderer = H.SVGRenderer, VMLRenderer = H.VMLRenderer;
  3864. if (VMLRenderer) {
  3865. H.setOptions({ animate: false });
  3866. VMLRenderer.prototype.face3d = SVGRenderer.prototype.face3d;
  3867. VMLRenderer.prototype.polyhedron = SVGRenderer.prototype.polyhedron;
  3868. VMLRenderer.prototype.elements3d = SVGRenderer.prototype.elements3d;
  3869. VMLRenderer.prototype.element3d = SVGRenderer.prototype.element3d;
  3870. VMLRenderer.prototype.cuboid = SVGRenderer.prototype.cuboid;
  3871. VMLRenderer.prototype.cuboidPath = SVGRenderer.prototype.cuboidPath;
  3872. VMLRenderer.prototype.toLinePath = SVGRenderer.prototype.toLinePath;
  3873. VMLRenderer.prototype.toLineSegments = SVGRenderer.prototype.toLineSegments;
  3874. VMLRenderer.prototype.arc3d = function (shapeArgs) {
  3875. var result = SVGRenderer.prototype.arc3d.call(this, shapeArgs);
  3876. result.css({ zIndex: result.zIndex });
  3877. return result;
  3878. };
  3879. H.VMLRenderer.prototype.arc3dPath = H.SVGRenderer.prototype.arc3dPath;
  3880. /* eslint-disable no-invalid-this */
  3881. addEvent(Axis, 'render', function () {
  3882. // VML doesn't support a negative z-index
  3883. if (this.sideFrame) {
  3884. this.sideFrame.css({ zIndex: 0 });
  3885. this.sideFrame.front.attr({
  3886. fill: this.sideFrame.color
  3887. });
  3888. }
  3889. if (this.bottomFrame) {
  3890. this.bottomFrame.css({ zIndex: 1 });
  3891. this.bottomFrame.front.attr({
  3892. fill: this.bottomFrame.color
  3893. });
  3894. }
  3895. if (this.backFrame) {
  3896. this.backFrame.css({ zIndex: 0 });
  3897. this.backFrame.front.attr({
  3898. fill: this.backFrame.color
  3899. });
  3900. }
  3901. });
  3902. /* eslint-enable no-invalid-this */
  3903. }
  3904. });
  3905. _registerModule(_modules, 'masters/highcharts-3d.src.js', [], function () {
  3906. });
  3907. }));