Axis3D.js 23 KB

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