Axis3D.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /* *
  2. *
  3. * (c) 2010-2020 Torstein Honsi
  4. *
  5. * Extenstion for 3d axes
  6. *
  7. * License: www.highcharts.com/license
  8. *
  9. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  10. *
  11. * */
  12. 'use strict';
  13. import H from '../parts/Globals.js';
  14. import Tick from '../parts/Tick.js';
  15. import Tick3D from './Tick3D.js';
  16. import U from '../parts/Utilities.js';
  17. var addEvent = U.addEvent, merge = U.merge, pick = U.pick, wrap = U.wrap;
  18. var deg2rad = H.deg2rad, perspective = H.perspective, perspective3D = H.perspective3D, shapeArea = H.shapeArea;
  19. /* eslint-disable valid-jsdoc */
  20. /**
  21. * Adds 3D support to axes.
  22. * @private
  23. * @class
  24. */
  25. var Axis3DAdditions = /** @class */ (function () {
  26. /* *
  27. *
  28. * Constructors
  29. *
  30. * */
  31. /**
  32. * @private
  33. */
  34. function Axis3DAdditions(axis) {
  35. this.axis = axis;
  36. }
  37. /* *
  38. *
  39. * Functions
  40. *
  41. * */
  42. /**
  43. * @private
  44. * @param {Highcharts.Axis} axis
  45. * Related axis.
  46. * @param {Highcharts.Position3dObject} pos
  47. * Position to fix.
  48. * @param {boolean} [isTitle]
  49. * Whether this is a title position.
  50. * @return {Highcharts.Position3dObject}
  51. * Fixed position.
  52. */
  53. Axis3DAdditions.prototype.fix3dPosition = function (pos, isTitle) {
  54. var axis3D = this;
  55. var axis = axis3D.axis;
  56. var chart = axis.chart;
  57. // Do not do this if the chart is not 3D
  58. if (axis.coll === 'colorAxis' ||
  59. !chart.chart3d ||
  60. !chart.is3d()) {
  61. return pos;
  62. }
  63. var 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.chart3d.frame3d, plotLeft = chart.plotLeft, plotRight = chart.plotWidth + plotLeft, plotTop = chart.plotTop, plotBottom = chart.plotHeight + plotTop,
  64. // Indicates that we are labelling an X or Z axis on the "back" of
  65. // the chart
  66. reverseFlap = false, offsetX = 0, offsetY = 0, vecX, vecY = { x: 0, y: 1, z: 0 };
  67. pos = axis.axis3D.swapZ({ x: pos.x, y: pos.y, z: 0 });
  68. if (axis.isZAxis) { // Z Axis
  69. if (axis.opposite) {
  70. if (frame.axes.z.top === null) {
  71. return {};
  72. }
  73. offsetY = pos.y - plotTop;
  74. pos.x = frame.axes.z.top.x;
  75. pos.y = frame.axes.z.top.y;
  76. vecX = frame.axes.z.top.xDir;
  77. reverseFlap = !frame.top.frontFacing;
  78. }
  79. else {
  80. if (frame.axes.z.bottom === null) {
  81. return {};
  82. }
  83. offsetY = pos.y - plotBottom;
  84. pos.x = frame.axes.z.bottom.x;
  85. pos.y = frame.axes.z.bottom.y;
  86. vecX = frame.axes.z.bottom.xDir;
  87. reverseFlap = !frame.bottom.frontFacing;
  88. }
  89. }
  90. else if (axis.horiz) { // X Axis
  91. if (axis.opposite) {
  92. if (frame.axes.x.top === null) {
  93. return {};
  94. }
  95. offsetY = pos.y - plotTop;
  96. pos.y = frame.axes.x.top.y;
  97. pos.z = frame.axes.x.top.z;
  98. vecX = frame.axes.x.top.xDir;
  99. reverseFlap = !frame.top.frontFacing;
  100. }
  101. else {
  102. if (frame.axes.x.bottom === null) {
  103. return {};
  104. }
  105. offsetY = pos.y - plotBottom;
  106. pos.y = frame.axes.x.bottom.y;
  107. pos.z = frame.axes.x.bottom.z;
  108. vecX = frame.axes.x.bottom.xDir;
  109. reverseFlap = !frame.bottom.frontFacing;
  110. }
  111. }
  112. else { // Y Axis
  113. if (axis.opposite) {
  114. if (frame.axes.y.right === null) {
  115. return {};
  116. }
  117. offsetX = pos.x - plotRight;
  118. pos.x = frame.axes.y.right.x;
  119. pos.z = frame.axes.y.right.z;
  120. vecX = frame.axes.y.right.xDir;
  121. // Rotate 90º on opposite edge
  122. vecX = { x: vecX.z, y: vecX.y, z: -vecX.x };
  123. }
  124. else {
  125. if (frame.axes.y.left === null) {
  126. return {};
  127. }
  128. offsetX = pos.x - plotLeft;
  129. pos.x = frame.axes.y.left.x;
  130. pos.z = frame.axes.y.left.z;
  131. vecX = frame.axes.y.left.xDir;
  132. }
  133. }
  134. if (positionMode === 'chart') {
  135. // Labels preserve their direction relative to the chart
  136. // nothing to do
  137. }
  138. else if (positionMode === 'flap') {
  139. // Labels are rotated around the axis direction to face the screen
  140. if (!axis.horiz) { // Y Axis
  141. vecX = { x: Math.cos(beta), y: 0, z: Math.sin(beta) };
  142. }
  143. else { // X and Z Axis
  144. var sin = Math.sin(alpha);
  145. var cos = Math.cos(alpha);
  146. if (axis.opposite) {
  147. sin = -sin;
  148. }
  149. if (reverseFlap) {
  150. sin = -sin;
  151. }
  152. vecY = { x: vecX.z * sin, y: cos, z: -vecX.x * sin };
  153. }
  154. }
  155. else if (positionMode === 'ortho') {
  156. // Labels will be rotated to be ortogonal to the axis
  157. if (!axis.horiz) { // Y Axis
  158. vecX = { x: Math.cos(beta), y: 0, z: Math.sin(beta) };
  159. }
  160. else { // X and Z Axis
  161. var sina = Math.sin(alpha);
  162. var cosa = Math.cos(alpha);
  163. var sinb = Math.sin(beta);
  164. var cosb = Math.cos(beta);
  165. var vecZ = { x: sinb * cosa, y: -sina, z: -cosa * cosb };
  166. vecY = {
  167. x: vecX.y * vecZ.z - vecX.z * vecZ.y,
  168. y: vecX.z * vecZ.x - vecX.x * vecZ.z,
  169. z: vecX.x * vecZ.y - vecX.y * vecZ.x
  170. };
  171. var scale = 1 / Math.sqrt(vecY.x * vecY.x + vecY.y * vecY.y + vecY.z * vecY.z);
  172. if (reverseFlap) {
  173. scale = -scale;
  174. }
  175. vecY = { x: scale * vecY.x, y: scale * vecY.y, z: scale * vecY.z };
  176. }
  177. }
  178. else { // positionMode == 'offset'
  179. // Labels will be skewd to maintain vertical / horizontal offsets
  180. // from axis
  181. if (!axis.horiz) { // Y Axis
  182. vecX = { x: Math.cos(beta), y: 0, z: Math.sin(beta) };
  183. }
  184. else { // X and Z Axis
  185. vecY = {
  186. x: Math.sin(beta) * Math.sin(alpha),
  187. y: Math.cos(alpha),
  188. z: -Math.cos(beta) * Math.sin(alpha)
  189. };
  190. }
  191. }
  192. pos.x += offsetX * vecX.x + offsetY * vecY.x;
  193. pos.y += offsetX * vecX.y + offsetY * vecY.y;
  194. pos.z += offsetX * vecX.z + offsetY * vecY.z;
  195. var projected = perspective([pos], axis.chart)[0];
  196. if (skew) {
  197. // Check if the label text would be mirrored
  198. var isMirrored = shapeArea(perspective([
  199. pos,
  200. { x: pos.x + vecX.x, y: pos.y + vecX.y, z: pos.z + vecX.z },
  201. { x: pos.x + vecY.x, y: pos.y + vecY.y, z: pos.z + vecY.z }
  202. ], axis.chart)) < 0;
  203. if (isMirrored) {
  204. vecX = { x: -vecX.x, y: -vecX.y, z: -vecX.z };
  205. }
  206. var pointsProjected = perspective([
  207. { x: pos.x, y: pos.y, z: pos.z },
  208. { x: pos.x + vecX.x, y: pos.y + vecX.y, z: pos.z + vecX.z },
  209. { x: pos.x + vecY.x, y: pos.y + vecY.y, z: pos.z + vecY.z }
  210. ], axis.chart);
  211. projected.matrix = [
  212. pointsProjected[1].x - pointsProjected[0].x,
  213. pointsProjected[1].y - pointsProjected[0].y,
  214. pointsProjected[2].x - pointsProjected[0].x,
  215. pointsProjected[2].y - pointsProjected[0].y,
  216. projected.x,
  217. projected.y
  218. ];
  219. projected.matrix[4] -= projected.x * projected.matrix[0] +
  220. projected.y * projected.matrix[2];
  221. projected.matrix[5] -= projected.x * projected.matrix[1] +
  222. projected.y * projected.matrix[3];
  223. }
  224. return projected;
  225. };
  226. /**
  227. * @private
  228. */
  229. Axis3DAdditions.prototype.swapZ = function (p, insidePlotArea) {
  230. var axis = this.axis;
  231. if (axis.isZAxis) {
  232. var plotLeft = insidePlotArea ? 0 : axis.chart.plotLeft;
  233. return {
  234. x: plotLeft + p.z,
  235. y: p.y,
  236. z: p.x - plotLeft
  237. };
  238. }
  239. return p;
  240. };
  241. return Axis3DAdditions;
  242. }());
  243. /**
  244. * Axis with 3D support.
  245. * @private
  246. * @class
  247. */
  248. var Axis3D = /** @class */ (function () {
  249. function Axis3D() {
  250. }
  251. /* *
  252. *
  253. * Static Functions
  254. *
  255. * */
  256. /**
  257. * Extends axis class with 3D support.
  258. * @private
  259. */
  260. Axis3D.compose = function (AxisClass) {
  261. merge(true, AxisClass.defaultOptions, Axis3D.defaultOptions);
  262. AxisClass.keepProps.push('axis3D');
  263. addEvent(AxisClass, 'init', Axis3D.onInit);
  264. addEvent(AxisClass, 'afterSetOptions', Axis3D.onAfterSetOptions);
  265. addEvent(AxisClass, 'drawCrosshair', Axis3D.onDrawCrosshair);
  266. addEvent(AxisClass, 'destroy', Axis3D.onDestroy);
  267. var axisProto = AxisClass.prototype;
  268. wrap(axisProto, 'getLinePath', Axis3D.wrapGetLinePath);
  269. wrap(axisProto, 'getPlotBandPath', Axis3D.wrapGetPlotBandPath);
  270. wrap(axisProto, 'getPlotLinePath', Axis3D.wrapGetPlotLinePath);
  271. wrap(axisProto, 'getSlotWidth', Axis3D.wrapGetSlotWidth);
  272. wrap(axisProto, 'getTitlePosition', Axis3D.wrapGetTitlePosition);
  273. Tick3D.compose(Tick);
  274. };
  275. /**
  276. * @private
  277. */
  278. Axis3D.onAfterSetOptions = function () {
  279. var axis = this;
  280. var chart = axis.chart;
  281. var options = axis.options;
  282. if (chart.is3d && chart.is3d() && axis.coll !== 'colorAxis') {
  283. options.tickWidth = pick(options.tickWidth, 0);
  284. options.gridLineWidth = pick(options.gridLineWidth, 1);
  285. }
  286. };
  287. /**
  288. * @private
  289. */
  290. Axis3D.onDestroy = function () {
  291. ['backFrame', 'bottomFrame', 'sideFrame'].forEach(function (prop) {
  292. if (this[prop]) {
  293. this[prop] = this[prop].destroy();
  294. }
  295. }, this);
  296. };
  297. /**
  298. * @private
  299. */
  300. Axis3D.onDrawCrosshair = function (e) {
  301. var axis = this;
  302. if (axis.chart.is3d() &&
  303. axis.coll !== 'colorAxis') {
  304. if (e.point) {
  305. e.point.crosshairPos = axis.isXAxis ?
  306. e.point.axisXpos :
  307. axis.len - e.point.axisYpos;
  308. }
  309. }
  310. };
  311. /**
  312. * @private
  313. */
  314. Axis3D.onInit = function () {
  315. var axis = this;
  316. if (!axis.axis3D) {
  317. axis.axis3D = new Axis3DAdditions(axis);
  318. }
  319. };
  320. /**
  321. * Do not draw axislines in 3D.
  322. * @private
  323. */
  324. Axis3D.wrapGetLinePath = function (proceed) {
  325. var axis = this;
  326. // Do not do this if the chart is not 3D
  327. if (!axis.chart.is3d() || axis.coll === 'colorAxis') {
  328. return proceed.apply(axis, [].slice.call(arguments, 1));
  329. }
  330. return [];
  331. };
  332. /**
  333. * @private
  334. */
  335. Axis3D.wrapGetPlotBandPath = function (proceed) {
  336. // Do not do this if the chart is not 3D
  337. if (!this.chart.is3d() || this.coll === 'colorAxis') {
  338. return proceed.apply(this, [].slice.call(arguments, 1));
  339. }
  340. var args = arguments, from = args[1], to = args[2], path = [], fromPath = this.getPlotLinePath({ value: from }), toPath = this.getPlotLinePath({ value: to });
  341. if (fromPath && toPath) {
  342. for (var i = 0; i < fromPath.length; i += 2) {
  343. var fromStartSeg = fromPath[i], fromEndSeg = fromPath[i + 1], toStartSeg = toPath[i], toEndSeg = toPath[i + 1];
  344. if (fromStartSeg[0] === 'M' &&
  345. fromEndSeg[0] === 'L' &&
  346. toStartSeg[0] === 'M' &&
  347. toEndSeg[0] === 'L') {
  348. path.push(fromStartSeg, fromEndSeg, toEndSeg,
  349. // lineTo instead of moveTo
  350. ['L', toStartSeg[1], toStartSeg[2]], ['Z']);
  351. }
  352. }
  353. }
  354. return path;
  355. };
  356. /**
  357. * @private
  358. */
  359. Axis3D.wrapGetPlotLinePath = function (proceed) {
  360. var axis = this;
  361. var axis3D = axis.axis3D;
  362. var chart = axis.chart;
  363. var path = proceed.apply(axis, [].slice.call(arguments, 1));
  364. // Do not do this if the chart is not 3D
  365. if (axis.coll === 'colorAxis' ||
  366. !chart.chart3d ||
  367. !chart.is3d()) {
  368. return path;
  369. }
  370. if (path === null) {
  371. return path;
  372. }
  373. var options3d = chart.options.chart.options3d, d = axis.isZAxis ? chart.plotWidth : options3d.depth, frame = chart.chart3d.frame3d, startSegment = path[0], endSegment = path[1], pArr, pathSegments = [];
  374. if (startSegment[0] === 'M' && endSegment[0] === 'L') {
  375. pArr = [
  376. axis3D.swapZ({ x: startSegment[1], y: startSegment[2], z: 0 }),
  377. axis3D.swapZ({ x: startSegment[1], y: startSegment[2], z: d }),
  378. axis3D.swapZ({ x: endSegment[1], y: endSegment[2], z: 0 }),
  379. axis3D.swapZ({ x: endSegment[1], y: endSegment[2], z: d })
  380. ];
  381. if (!this.horiz) { // Y-Axis
  382. if (frame.front.visible) {
  383. pathSegments.push(pArr[0], pArr[2]);
  384. }
  385. if (frame.back.visible) {
  386. pathSegments.push(pArr[1], pArr[3]);
  387. }
  388. if (frame.left.visible) {
  389. pathSegments.push(pArr[0], pArr[1]);
  390. }
  391. if (frame.right.visible) {
  392. pathSegments.push(pArr[2], pArr[3]);
  393. }
  394. }
  395. else if (this.isZAxis) { // Z-Axis
  396. if (frame.left.visible) {
  397. pathSegments.push(pArr[0], pArr[2]);
  398. }
  399. if (frame.right.visible) {
  400. pathSegments.push(pArr[1], pArr[3]);
  401. }
  402. if (frame.top.visible) {
  403. pathSegments.push(pArr[0], pArr[1]);
  404. }
  405. if (frame.bottom.visible) {
  406. pathSegments.push(pArr[2], pArr[3]);
  407. }
  408. }
  409. else { // X-Axis
  410. if (frame.front.visible) {
  411. pathSegments.push(pArr[0], pArr[2]);
  412. }
  413. if (frame.back.visible) {
  414. pathSegments.push(pArr[1], pArr[3]);
  415. }
  416. if (frame.top.visible) {
  417. pathSegments.push(pArr[0], pArr[1]);
  418. }
  419. if (frame.bottom.visible) {
  420. pathSegments.push(pArr[2], pArr[3]);
  421. }
  422. }
  423. pathSegments = perspective(pathSegments, this.chart, false);
  424. }
  425. return chart.renderer.toLineSegments(pathSegments);
  426. };
  427. /**
  428. * Wrap getSlotWidth function to calculate individual width value for each
  429. * slot (#8042).
  430. * @private
  431. */
  432. Axis3D.wrapGetSlotWidth = function (proceed, tick) {
  433. var axis = this;
  434. var chart = axis.chart;
  435. var ticks = axis.ticks;
  436. var gridGroup = axis.gridGroup;
  437. if (axis.categories &&
  438. chart.frameShapes &&
  439. chart.is3d() &&
  440. gridGroup &&
  441. tick &&
  442. tick.label) {
  443. var firstGridLine = gridGroup.element.childNodes[0].getBBox(), frame3DLeft = chart.frameShapes.left.getBBox(), options3d = chart.options.chart.options3d, origin = {
  444. x: chart.plotWidth / 2,
  445. y: chart.plotHeight / 2,
  446. z: options3d.depth / 2,
  447. vd: pick(options3d.depth, 1) * pick(options3d.viewDistance, 0)
  448. }, labelPos, prevLabelPos, nextLabelPos, slotWidth, tickId = tick.pos, prevTick = ticks[tickId - 1], nextTick = ticks[tickId + 1];
  449. // Check whether the tick is not the first one and previous tick
  450. // exists, then calculate position of previous label.
  451. if (tickId !== 0 && prevTick && prevTick.label.xy) {
  452. prevLabelPos = perspective3D({
  453. x: prevTick.label.xy.x,
  454. y: prevTick.label.xy.y,
  455. z: null
  456. }, origin, origin.vd);
  457. }
  458. // If next label position is defined, then recalculate its position
  459. // basing on the perspective.
  460. if (nextTick && nextTick.label.xy) {
  461. nextLabelPos = perspective3D({
  462. x: nextTick.label.xy.x,
  463. y: nextTick.label.xy.y,
  464. z: null
  465. }, origin, origin.vd);
  466. }
  467. labelPos = {
  468. x: tick.label.xy.x,
  469. y: tick.label.xy.y,
  470. z: null
  471. };
  472. labelPos = perspective3D(labelPos, origin, origin.vd);
  473. // If tick is first one, check whether next label position is
  474. // already calculated, then return difference between the first and
  475. // the second label. If there is no next label position calculated,
  476. // return the difference between the first grid line and left 3d
  477. // frame.
  478. slotWidth = Math.abs(prevLabelPos ?
  479. labelPos.x - prevLabelPos.x : nextLabelPos ?
  480. nextLabelPos.x - labelPos.x :
  481. firstGridLine.x - frame3DLeft.x);
  482. return slotWidth;
  483. }
  484. return proceed.apply(axis, [].slice.call(arguments, 1));
  485. };
  486. /**
  487. * @private
  488. */
  489. Axis3D.wrapGetTitlePosition = function (proceed) {
  490. var pos = proceed.apply(this, [].slice.call(arguments, 1));
  491. return this.axis3D ?
  492. this.axis3D.fix3dPosition(pos, true) :
  493. pos;
  494. };
  495. /* *
  496. *
  497. * Static Properties
  498. *
  499. * */
  500. /**
  501. * @optionparent xAxis
  502. */
  503. Axis3D.defaultOptions = {
  504. labels: {
  505. /**
  506. * Defines how the labels are be repositioned according to the 3D
  507. * chart orientation.
  508. *
  509. * - `'offset'`: Maintain a fixed horizontal/vertical distance from
  510. * the tick marks, despite the chart orientation. This is the
  511. * backwards compatible behavior, and causes skewing of X and Z
  512. * axes.
  513. *
  514. * - `'chart'`: Preserve 3D position relative to the chart. This
  515. * looks nice, but hard to read if the text isn't forward-facing.
  516. *
  517. * - `'flap'`: Rotated text along the axis to compensate for the
  518. * chart orientation. This tries to maintain text as legible as
  519. * possible on all orientations.
  520. *
  521. * - `'ortho'`: Rotated text along the axis direction so that the
  522. * labels are orthogonal to the axis. This is very similar to
  523. * `'flap'`, but prevents skewing the labels (X and Y scaling are
  524. * still present).
  525. *
  526. * @sample highcharts/3d/skewed-labels/
  527. * Skewed labels
  528. *
  529. * @since 5.0.15
  530. * @validvalue ['offset', 'chart', 'flap', 'ortho']
  531. * @product highcharts
  532. * @requires highcharts-3d
  533. */
  534. position3d: 'offset',
  535. /**
  536. * If enabled, the axis labels will skewed to follow the
  537. * perspective.
  538. *
  539. * This will fix overlapping labels and titles, but texts become
  540. * less legible due to the distortion.
  541. *
  542. * The final appearance depends heavily on `labels.position3d`.
  543. *
  544. * @sample highcharts/3d/skewed-labels/
  545. * Skewed labels
  546. *
  547. * @since 5.0.15
  548. * @product highcharts
  549. * @requires highcharts-3d
  550. */
  551. skew3d: false
  552. },
  553. title: {
  554. /**
  555. * Defines how the title is repositioned according to the 3D chart
  556. * orientation.
  557. *
  558. * - `'offset'`: Maintain a fixed horizontal/vertical distance from
  559. * the tick marks, despite the chart orientation. This is the
  560. * backwards compatible behavior, and causes skewing of X and Z
  561. * axes.
  562. *
  563. * - `'chart'`: Preserve 3D position relative to the chart. This
  564. * looks nice, but hard to read if the text isn't forward-facing.
  565. *
  566. * - `'flap'`: Rotated text along the axis to compensate for the
  567. * chart orientation. This tries to maintain text as legible as
  568. * possible on all orientations.
  569. *
  570. * - `'ortho'`: Rotated text along the axis direction so that the
  571. * labels are orthogonal to the axis. This is very similar to
  572. * `'flap'`, but prevents skewing the labels (X and Y scaling are
  573. * still present).
  574. *
  575. * - `undefined`: Will use the config from `labels.position3d`
  576. *
  577. * @sample highcharts/3d/skewed-labels/
  578. * Skewed labels
  579. *
  580. * @type {"offset"|"chart"|"flap"|"ortho"|null}
  581. * @since 5.0.15
  582. * @product highcharts
  583. * @requires highcharts-3d
  584. */
  585. position3d: null,
  586. /**
  587. * If enabled, the axis title will skewed to follow the perspective.
  588. *
  589. * This will fix overlapping labels and titles, but texts become
  590. * less legible due to the distortion.
  591. *
  592. * The final appearance depends heavily on `title.position3d`.
  593. *
  594. * A `null` value will use the config from `labels.skew3d`.
  595. *
  596. * @sample highcharts/3d/skewed-labels/
  597. * Skewed labels
  598. *
  599. * @type {boolean|null}
  600. * @since 5.0.15
  601. * @product highcharts
  602. * @requires highcharts-3d
  603. */
  604. skew3d: null
  605. }
  606. };
  607. return Axis3D;
  608. }());
  609. export default Axis3D;