UNPKG

@syncfusion/ej2-charts

Version:

Feature-rich chart control with built-in support for over 25 chart types, technical indictors, trendline, zooming, tooltip, selection, crosshair and trackball.

1,244 lines 90 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; import { measureText } from '@syncfusion/ej2-svg-base'; import { appendChildElement, colorNameToHex } from '../../common/utils/helper'; import { isNullOrUndefined } from '@syncfusion/ej2-base'; /** * Represents a 3D rendering configuration for the EJ3D rendering engine. * */ var Chart3DRender = /** @class */ (function () { function Chart3DRender() { this.transform = null; // Chart3DBasicTransform } return Chart3DRender; }()); export { Chart3DRender }; var chart3DRender = new Chart3DRender(); /** * Represents a three-dimensional vector in space. */ var Vector3D = /** @class */ (function () { /** * Constructs a new Vector3D instance. * * @constructor * @param {number | { x: number, y: number }} pointX - Either an object with x and y properties or the x-coordinate. * @param {number} [vy] - The y-coordinate (if the first parameter is a number). * @param {number} [vz] - The z-coordinate (if the first parameter is a number). */ function Vector3D(pointX, vy, vz) { /** The x-coordinate of the vector. */ this.x = 0; /** The y-coordinate of the vector. */ this.y = 0; /** The z-coordinate of the vector. */ this.z = 0; /** A small value used for epsilon comparisons to handle floating-point inaccuracies.*/ this.epsilon = 0.00001; this.x = pointX; this.y = vy || 0; this.z = vz || 0; } /** * Checks if a vector is valid (not NaN for any component). * * @param {Chart3DVector} point - The vector to check. * @returns {boolean} - True if the vector is valid, false otherwise. */ Vector3D.prototype.isValid = function (point) { return !isNaN(point.x) && !isNaN(point.y) && !isNaN(point.z); }; /** * Creates a new Vector3D instance from provided coordinates. * * @param {number | { x: number, y: number }} vx - Either an object with x and y properties or the x-coordinate. * @param {number} vy - The y-coordinate. * @param {number} vz - The z-coordinate. * @returns {Chart3DVector} - The new Vector3D instance. */ Vector3D.prototype.vector3D = function (vx, vy, vz) { this.x = vx; this.y = vy; this.z = vz; return { x: this.x, y: this.y, z: this.z }; }; /** * Subtracts one vector from another and returns the result. * * @param {Chart3DVector} v1 - The first vector. * @param {Chart3DVector} v2 - The second vector to subtract from the first. * @returns {Chart3DVector} - The resulting vector. */ Vector3D.prototype.vector3DMinus = function (v1, v2) { return this.vector3D(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); }; /** * Adds two vectors and returns the result. * * @param {Chart3DVector} v1 - The first vector. * @param {Chart3DVector} v2 - The second vector to add to the first. * @returns {Chart3DVector} - The resulting vector. */ Vector3D.prototype.vector3DPlus = function (v1, v2) { return this.vector3D(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); }; /** * Multiplies two vectors using the cross product and returns the result. * * @param {Chart3DVector} v1 - The first vector. * @param {Chart3DVector} v2 - The second vector. * @returns {Chart3DVector} - The resulting vector. */ Vector3D.prototype.vector3DMultiply = function (v1, v2) { var x = v1.y * v2.z - v2.y * v1.z; var y = v1.z * v2.x - v2.z * v1.x; var z = v1.x * v2.y - v2.x * v1.y; return this.vector3D(x, y, z); }; /** * Calculates the dot product of two vectors. * * @param {Chart3DVector} v1 - The first vector. * @param {Chart3DVector} v2 - The second vector. * @returns {number} - The dot product. */ Vector3D.prototype.vector3DAdd = function (v1, v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }; /** * Multiplies a vector by a scalar value. * * @param {Chart3DVector} v1 - The vector to multiply. * @param {number} value - The scalar value. * @returns {Chart3DVector} - The resulting vector. */ Vector3D.prototype.vector3DStarMultiply = function (v1, value) { var x = v1.x * value; var y = v1.y * value; var z = v1.z * value; return this.vector3D(x, y, z); }; /** * Calculates the length of a vector. * * @param {Chart3DVector} vector - The vector to calculate the length of. * @returns {number} - The length of the vector. */ Vector3D.prototype.getLength = function (vector) { var sqt = this.vector3DAdd(vector, vector); return Math.sqrt(sqt); }; /** * Normalizes the vector to have a length of 1. * * @returns {void} */ Vector3D.prototype.normalize = function () { var length = this.getLength(this); this.x /= length; this.y /= length; this.z /= length; }; /** * Calculates the normal vector of a triangle defined by three vectors. * * @param {Chart3DVector} v1 - The first vertex of the triangle. * @param {Chart3DVector} v2 - The second vertex of the triangle. * @param {Chart3DVector} v3 - The third vertex of the triangle. * @returns {Chart3DVector} - The normal vector of the triangle. */ Vector3D.prototype.getNormal = function (v1, v2, v3) { var vector4 = this.vector3DMinus(v1, v2); var vector5 = this.vector3DMinus(v3, v2); var n = this.vector3DMultiply(vector4, vector5); var length = this.getLength(n); if (length < this.epsilon) { return this.vector3D(0, 0, 0); } return this.vector3D(n.x / length, n.y / length, n.z / length); }; return Vector3D; }()); export { Vector3D }; /** * Represents a 3x3 or 4x4 matrix in 3D space and provides various matrix operations. * */ var Matrix3D = /** @class */ (function () { function Matrix3D() { /** The size of the matrix, which is set to 4 by default. */ this.matrixSize = 4; } /** * Creates a 3D matrix with the specified size. * * @param {number} size - The size of the matrix. * @returns {number[][]} - The created 3D matrix. */ Matrix3D.prototype.matrix3D = function (size) { var matrixData = []; for (var i = 0; i < size; i++) { matrixData[i] = this.createArray(size); } return matrixData; }; /** * Checks if a matrix is an affine matrix. * * @param {number[][]} matrixData - The matrix to check. * @returns {boolean} - True if the matrix is an affine matrix, false otherwise. */ Matrix3D.prototype.isAffine = function (matrixData) { return matrixData[0][3] === 0 && matrixData[1][3] === 0 && matrixData[2][3] === 0 && matrixData[3][3] === 1; }; /** * Creates a new array with zeros. * * @param {number} initialSize - The size of the array. * @returns {number[]} - The created array. */ Matrix3D.prototype.createArray = function (initialSize) { var matrixData = []; for (var index = 0; index < initialSize; ++index) { matrixData[index] = 0; } return matrixData; }; /** * Gets the identity matrix. * * @returns {number[][]} -The identity matrix. */ Matrix3D.prototype.getIdentity = function () { var matrixData = this.matrix3D(this.matrixSize); for (var i = 0; i < this.matrixSize; i++) { matrixData[i][i] = 1.0; } return matrixData; }; /** * Gets the interval of a matrix. * * @param {number[][]} matrix - The matrix to get the interval for. * @returns {number[][]} - The interval matrix. */ Matrix3D.prototype.getInterval = function (matrix) { var matrixData = this.getIdentity(); for (var i = 0; i < this.matrixSize; i++) { for (var j = 0; j < this.matrixSize; j++) { matrixData[i][j] = this.getMinor(matrix, i, j); } } matrixData = this.transposed(matrixData); matrixData = this.getMatrixMultiple(1 / this.getDeterminant(matrix), matrixData); return matrixData; }; /** * Multiplies all elements of a matrix by a factor. * * @param {number} factor - The factor to multiply with. * @param {number[][]} matrix - The matrix to multiply. * @returns {number[][]} - The resulting matrix. */ Matrix3D.prototype.getMatrixMultiple = function (factor, matrix) { for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix[i].length; j++) { matrix[i][j] = matrix[i][j] * factor; } } return matrix; }; /** * Multiplies a matrix by a vector. * * @param {number[][]} matrix - The matrix. * @param {Chart3DVector} point - The vector to multiply with. * @returns {Chart3DVector} - The resulting vector. */ Matrix3D.prototype.getMatrixVectorMultiple = function (matrix, point) { var x = matrix[0][0] * point.x + matrix[1][0] * point.y + matrix[2][0] * point.z + matrix[3][0]; var y = matrix[0][1] * point.x + matrix[1][1] * point.y + matrix[2][1] * point.z + matrix[3][1]; var z = matrix[0][2] * point.x + matrix[1][2] * point.y + matrix[2][2] * point.z + matrix[3][2]; if (!this.isAffine(matrix)) { var c = 1 / (matrix[0][3] * point.x + matrix[1][3] * point.y + matrix[2][3] * point.z + matrix[3][3]); x *= c; y *= c; z *= c; } return { x: x, y: y, z: z }; }; /** * Multiplies a matrix by a vector and applies translation. * * @param {number[][]} matrix - The matrix. * @param {Chart3DVector} vector - The vector to multiply with. * @returns {Vector3D} - The resulting vector. */ Matrix3D.prototype.getMatrixVectorAnd = function (matrix, vector) { var x = matrix[0][0] * vector.x + matrix[1][0] * vector.y + matrix[2][0] * vector.z; var y = matrix[0][1] * vector.x + matrix[1][1] * vector.y + matrix[2][1] * vector.z; var z = matrix[0][2] * vector.x + matrix[1][2] * vector.y + matrix[2][2] * vector.z; return new Vector3D(x, y, z); }; /** * Multiplies two matrices. * * @param {number[][]} matrix1 - The first matrix. * @param {number[][]} matrix2 - The second matrix. * @returns {number[][]} - The resulting matrix. */ Matrix3D.prototype.getMatrixMultiplication = function (matrix1, matrix2) { var result = this.getIdentity(); for (var i = 0; i < this.matrixSize; i++) { for (var j = 0; j < this.matrixSize; j++) { var value = 0; for (var k = 0; k < this.matrixSize; k++) { value += matrix1[k][j] * matrix2[i][k]; } result[i][j] = value; } } return result; }; /** * Gets the minor of a matrix. * * @param {number[][]} matrix - The matrix. * @param {number} columnIndex - The column index. * @param {number} rowIndex - The row index. * @returns {number} - The minor of the matrix. * @private */ Matrix3D.prototype.getMinor = function (matrix, columnIndex, rowIndex) { return ((columnIndex + rowIndex) % 2 === 0 ? 1 : -1) * this.getDeterminant(this.getMatrix(matrix, columnIndex, rowIndex)); }; /** * Gets a submatrix of a matrix. * * @param {number[][]} matrix - The matrix. * @param {number} columnIndex - The column index. * @param {number} rowIndex - The row index. * @returns {number[][]} - The submatrix. */ Matrix3D.prototype.getMatrix = function (matrix, columnIndex, rowIndex) { var count = matrix.length - 1; var subMatrix = this.createArray(count); for (var i = 0; i < count; i++) { var matrixColumn = i >= columnIndex ? i + 1 : i; subMatrix[i] = this.createArray(count); for (var j = 0; j < count; j++) { var matrixRow = j >= rowIndex ? j + 1 : j; subMatrix[i][j] = matrix[matrixColumn][matrixRow]; } } return subMatrix; }; /** * Gets the determinant of a matrix. * * @param {number[][]} matrix - The matrix. * @returns {number} - The determinant of the matrix. */ Matrix3D.prototype.getDeterminant = function (matrix) { var count = matrix.length; var determinant = 0; if (count < 2) { determinant = matrix[0][0]; } else { var k = 1; for (var i = 0; i < count; i++) { var submatrix = this.getMatrix(matrix, i, 0); determinant += k * matrix[i][0] * this.getDeterminant(submatrix); k = k > 0 ? -1 : 1; } } return determinant; }; /** * Transforms a matrix by translation. * * @param {number} x - The x-coordinate of the translation. * @param {number} y - The y-coordinate of the translation. * @param {number} z - The z-coordinate of the translation. * @returns {number[][]} - The transformed matrix. */ Matrix3D.prototype.transform = function (x, y, z) { var transformedMatrix = this.getIdentity(); transformedMatrix[3][0] = x; transformedMatrix[3][1] = y; transformedMatrix[3][2] = z; return transformedMatrix; }; /** * Creates a matrix for rotation around the y-axis. * * @param {number} angle - The angle of rotation. * @returns {number[][]} - The rotation matrix. */ Matrix3D.prototype.turn = function (angle) { var rotatedMatrix = this.getIdentity(); rotatedMatrix[0][0] = Math.cos(angle); rotatedMatrix[2][0] = -Math.sin(angle); rotatedMatrix[0][2] = Math.sin(angle); rotatedMatrix[2][2] = Math.cos(angle); return rotatedMatrix; }; /** * Creates a matrix for rotation around the x-axis. * * @param {number} angle - The angle of rotation. * @returns {number[][]} - The rotation matrix. */ Matrix3D.prototype.tilt = function (angle) { var rotatedMatrix = this.getIdentity(); rotatedMatrix[1][1] = Math.cos(angle); rotatedMatrix[2][1] = Math.sin(angle); rotatedMatrix[1][2] = -Math.sin(angle); rotatedMatrix[2][2] = Math.cos(angle); return rotatedMatrix; }; /** * Transposes a matrix. * * @param {number[][]} matrix3D - The matrix to transpose. * @returns {number[][]} - The transposed matrix. */ Matrix3D.prototype.transposed = function (matrix3D) { var transposedMatrix = this.getIdentity(); for (var i = 0; i < this.matrixSize; i++) { for (var j = 0; j < this.matrixSize; j++) { transposedMatrix[i][j] = matrix3D[j][i]; } } return transposedMatrix; }; return Matrix3D; }()); export { Matrix3D }; /** * Represents a 3D chart transformation utility that provides methods for transforming * and projecting 3D coordinates onto a 2D screen. * */ var ChartTransform3D = /** @class */ (function () { /** * Initializes a new instance of the `ChartTransform3D` class. */ function ChartTransform3D() { /** Represents the angle conversion factor from degrees to radians. */ this.toRadial = Math.PI / 180; this.vector = new Vector3D(0, 0, 0); this.matrixObj = new Matrix3D(); } /** * Creates a 3D transformation based on the specified size. * * @param {Size} size - The size of the viewing area. * @returns {Chart3DBasicTransform} - The 3D transformation. */ ChartTransform3D.prototype.transform3D = function (size) { return { viewingArea: size, rotation: 0, tilt: 0, depth: 0, perspectiveAngle: 0, needUpdate: true, centeredMatrix: this.matrixObj.getIdentity(), perspective: this.matrixObj.getIdentity(), resultMatrix: this.matrixObj.getIdentity(), viewMatrix: this.matrixObj.getIdentity() }; }; /** * Applies the specified 3D transformation to the current state. * * @param {Chart3DBasicTransform} transform - The 3D transformation to apply. * @returns {void} - The 3D transformation. */ ChartTransform3D.prototype.transform = function (transform) { this.setCenter(this.vector.vector3D(transform.viewingArea.width / 2, transform.viewingArea.height / 2, transform.depth / 2), transform); this.setViewMatrix(this.matrixObj.transform(0, 0, transform.depth), transform); this.setViewMatrix(this.matrixObj.getMatrixMultiplication(transform.viewMatrix, this.matrixObj.turn(-this.toRadial * transform.rotation)), transform); this.setViewMatrix(this.matrixObj.getMatrixMultiplication(transform.viewMatrix, this.matrixObj.tilt(-this.toRadial * transform.tilt)), transform); this.updatePerspective(transform.perspectiveAngle, transform); transform.needUpdate = true; }; /** * Updates the perspective matrix based on the specified angle. * * @param {number} angle - The perspective angle. * @param {Chart3DBasicTransform} transform - The 3D transformation. * @returns {void} */ ChartTransform3D.prototype.updatePerspective = function (angle, transform) { var width = (((transform.viewingArea.width + transform.viewingArea.height) * Math.tan(this.degreeToRadianConverter((180 - Math.abs(angle % 181)) / 2.0))) + (transform.depth * 2) / 2); transform.perspective[0][0] = width; transform.perspective[1][1] = width; transform.perspective[2][3] = 1; transform.perspective[3][3] = width; }; /** * Converts degrees to radians. * * @param {number} angle - The angle in degrees. * @returns {number} - The angle in radians. * @private */ ChartTransform3D.prototype.degreeToRadianConverter = function (angle) { return angle * Math.PI / 180; }; /** * Transforms a 3D vector to screen coordinates based on the current state. * * @param {Chart3DVector} vector3D - The 3D vector to transform. * @param {Chart3DBasicTransform} transform - The 3D transformation. * @param {Matrix3D} chartObj - Optional custom matrix object for transformation. * @returns {Chart3DLocation} - The screen coordinates. */ ChartTransform3D.prototype.toScreen = function (vector3D, transform, chartObj) { if (!chartObj) { transform.chartObj = this.matrixObj; vector3D = this.matrixObj.getMatrixVectorMultiple(this.result(transform), vector3D); } else { this.matrixObj = chartObj; vector3D = chartObj.getMatrixVectorMultiple(this.result(transform, chartObj), vector3D); } return { x: vector3D.x, y: vector3D.y }; }; /** * Sets the view matrix in the transformation state. * * @param {number[][]} matrix - The new view matrix. * @param {Chart3DBasicTransform} transform - The 3D transformation. * @returns {void} */ ChartTransform3D.prototype.setViewMatrix = function (matrix, transform) { if (transform.viewMatrix === matrix) { return; } transform.viewMatrix = matrix; transform.needUpdate = true; }; /** * Calculates the final result matrix based on the current state. * * @param {Chart3DBasicTransform} transform - The 3D transformation. * @param {Matrix3D} matrixobj - Optional custom matrix object for transformation. * @returns {number[][]} - The final result matrix. */ ChartTransform3D.prototype.result = function (transform, matrixobj) { var chartObj = transform.chartObj ? transform.chartObj : this.matrixObj; if (!chartObj) { chartObj = matrixobj; } if (!transform.needUpdate) { return transform.resultMatrix; } var matrixObj = this.matrixObj ? this.matrixObj : matrixobj; transform.resultMatrix = chartObj.getMatrixMultiplication(matrixObj.getInterval(transform.centeredMatrix), transform.perspective); transform.resultMatrix = chartObj.getMatrixMultiplication(transform.resultMatrix, transform.viewMatrix); transform.resultMatrix = chartObj.getMatrixMultiplication(transform.resultMatrix, transform.centeredMatrix); transform.needUpdate = false; return transform.resultMatrix; }; /** * Sets the center in the transformation state. * * @param {Chart3DVector} center - The new center vector. * @param {Chart3DBasicTransform} transform - The 3D transformation. * @returns {void} */ ChartTransform3D.prototype.setCenter = function (center, transform) { transform.centeredMatrix = this.matrixObj.transform(-center.x, -center.y, -center.z); transform.needUpdate = true; }; return ChartTransform3D; }()); export { ChartTransform3D }; /** * Represents a 3D graphics rendering utility for drawing and managing 3D elements in a chart. * */ var Graphics3D = /** @class */ (function () { function Graphics3D() { /** The vector class. */ this.vector = new Vector3D(0, 0, 0); } /** * Adds a visual polygon to the 3D chart and returns its identifier. * * @param {Chart3DPolygon} polygon - The polygon to add. * @param {Chart3D} chart - The 3D chart. * @returns {number} - The identifier of the added polygon. */ Graphics3D.prototype.addVisual = function (polygon, chart) { if (polygon == null || polygonObj.test()) { return -1; } return bspTreeObj.add(polygon, chart); }; /** * Prepares the view for rendering based on specified parameters. * * @param {number} perspectiveAngle - The perspective angle. * @param {number} depth - The depth of the view. * @param {number} rotation - The rotation angle. * @param {number} tilt - The tilt angle. * @param {Size} size - The size of the viewing area. * @param {Chart3D} chart - The 3D chart. * @returns {void} */ Graphics3D.prototype.prepareView = function (perspectiveAngle, depth, rotation, tilt, size, chart) { if (chart3DRender.transform == null) { chart3DRender.transform = chart.transform3D.transform3D(size); } else { chart3DRender.transform.viewingArea = size; } if (!chart3DRender.tree) { chart3DRender.tree = []; } chart3DRender.transform.rotation = rotation; chart3DRender.transform.tilt = tilt; chart3DRender.transform.depth = depth; chart3DRender.transform.perspectiveAngle = perspectiveAngle; chart.transform3D.transform(chart3DRender.transform); chart3DRender.tree[chart.chart3D.id] = bspTreeObj.build(); }; /** * Renders the 3D view on the specified panel element. * * @param {Element} panel - The panel element to render the view on. * @param {Chart3D} chart - The 3D chart. * @param {number} rotation - The rotation angle. * @param {number} tilt - The tilt angle. * @param {Size} size - The size of the viewing area. * @param {number} perspectiveAngle - The perspective angle. * @param {number} depth - The depth of the view. * @returns {void} */ Graphics3D.prototype.view = function (panel, chart, rotation, tilt, size, perspectiveAngle, depth) { var MaxValue = 32767; if (arguments.length === 2) { if (panel == null) { return; } var eyeVector = this.vector.vector3D(0, 0, MaxValue); this.drawNode3D(chart3DRender.tree[chart.chart3D.id], eyeVector, panel, chart); } else { if (panel == null) { return; } if (chart3DRender.transform == null) { chart3DRender.transform = chart.transform3D.transform3D(size); } else { chart3DRender.transform.viewingArea = size; } chart3DRender.transform.rotation = rotation; chart3DRender.transform.tilt = tilt; chart3DRender.transform.depth = depth; chart3DRender.transform.perspectiveAngle = perspectiveAngle; chart.transform3D.transform(chart3DRender.transform); var eye = this.vector.vector3D(0, 0, MaxValue); this.drawNode3D(chart3DRender.tree[chart.chart3D.id], eye, panel, chart); } }; /** * Draws a 3D element based on the specified Binary Space Partitioning Node. * * @param {Chart3DBspNode} bspElement - The Binary Space Partitioning Node representing the 3D element. * @param {Chart3D} chart - The 3D chart. * @returns {void} */ Graphics3D.prototype.draw3DElement = function (bspElement, chart) { if (bspElement.plane.element) { if (bspElement.plane.element.tag === 'text' || bspElement.plane.element.tag === 'dataLabel') { polygonObj.drawText(bspElement.plane, chart); } else if (bspElement.plane.element.tag === 'template') { polygonObj.drawTemplate(bspElement.plane, chart); } else { polygonObj.drawLine(bspElement.plane, chart); } } else { polygonObj.draw(bspElement.plane, chart); } }; /** * Draws the 3D nodes starting from the root based on the eye vector. * * @param {Chart3DBspNode} bspElement - The root Binary Space Partitioning Node. * @param {Chart3DVector} eyeVector - The eye vector. * @param {Element} panel - The panel element to render the view on. * @param {Chart3D} chart - The 3D chart. * @returns {void} */ Graphics3D.prototype.drawNode3D = function (bspElement, eyeVector, panel, chart) { if (bspElement === null || chart3DRender.transform == null) { return; } var isVector = true; while (isVector) { var r = vector.vector3DAdd(polygonObj.getNormal(chart.transform3D.result(chart3DRender.transform), bspElement.plane.vectorPoints), eyeVector); if (r > bspElement.plane.d) { if (bspElement.front != null) { this.drawNode3D(bspElement.front, eyeVector, panel, chart); } this.draw3DElement(bspElement, chart); if (bspElement.back != null) { bspElement = bspElement.back; continue; } } else { if (bspElement.back != null) { this.drawNode3D(bspElement.back, eyeVector, panel, chart); } this.draw3DElement(bspElement, chart); if (bspElement.front != null) { bspElement = bspElement.front; continue; } } break; } }; return Graphics3D; }()); export { Graphics3D }; /** * Represents a binary tree builder for 3D polygons in a chart. * */ var BinaryTreeBuilder = /** @class */ (function () { function BinaryTreeBuilder(chart) { /** A small value used for epsilon comparisons to handle floating-point inaccuracies.*/ this.epsilon = 0.0005; this.chart = chart; } /** * Adds a polygon to the binary tree and returns its index. * * @param {Chart3DPolygon} polygon - The polygon to add. * @param {Chart3D} chart - The 3D chart. * @returns {number} - The index of the added polygon. */ BinaryTreeBuilder.prototype.add = function (polygon, chart) { this.chart = chart; chart.polygons.push(polygon); return chart.polygons.length - 1; }; /** * Gets the next index considering the array length and the current index. * * @param {number} index - The current index. * @param {number} count - The length of the array. * @returns {number} - The next index. */ BinaryTreeBuilder.prototype.getNext = function (index, count) { if (index >= count) { return index - count; } if (index < 0) { return index + count; } return index; }; /** * Creates a PolyAttributes object based on the vector, index, and result. * * @param {Chart3DVector} point - The vector representing the point. * @param {number} index - The index of the point. * @param {string} result - The result classification. * @returns {Chart3DPolyAttributes} - The created PolyAttributes object. */ BinaryTreeBuilder.prototype.vector3DIndexClassification = function (point, index, result) { return { index: index, result: result, vector: point, isCuttingBackPoint: false, cuttingBackPairIndex: null, alreadyCutBack: false, isCuttingFrontPoint: false, cuttingFrontPairIndex: null, alreadyCutFront: false }; }; /** * Classifies a point relative to a polygon. * * @param {Chart3DVector} point - The point to classify. * @param {Chart3DPolygon} polygon - The polygon for classification. * @returns {string} - The classification result ('OnPlane', 'OnBack', 'OnFront'). */ BinaryTreeBuilder.prototype.classifyPoint = function (point, polygon) { var result = 'OnPlane'; var signedDistance = -polygon.d - vector.vector3DAdd(point, polygon.normal); if (signedDistance > this.epsilon) { result = 'OnBack'; } else if (signedDistance < -this.epsilon) { result = 'OnFront'; } return result; }; /** * Classifies a polygon relative to another polygon. * * @param {Chart3DPolygon} refPolygon - The reference polygon. * @param {Chart3DPolygon} classPolygon - The polygon to classify. * @returns {string} - The classification result ('OnPlane', 'ToRight', 'ToLeft', 'Unknown'). */ BinaryTreeBuilder.prototype.classifyPolygon = function (refPolygon, classPolygon) { var result = 'Unknown'; var points = classPolygon.points; if (points == null) { return result; } var onBack = 0; var onFront = 0; var onPlane = 0; var normal = refPolygon.normal; var polygonValue = refPolygon.d; for (var i = 0, len = points.length; i < len; i++) { var value = -polygonValue - vector.vector3DAdd(points[i], normal); if (value > this.epsilon) { onBack++; } else if (value < -this.epsilon) { onFront++; } else { onPlane++; } if (onBack > 0 && onFront > 0) { break; } } if (onPlane === points.length) { result = 'OnPlane'; } else if (onFront + onPlane === points.length) { result = 'ToRight'; } else if (onBack + onPlane === points.length) { result = 'ToLeft'; } else { result = 'Unknown'; } return result; }; /** * Splits a polygon into two parts based on another polygon. * * @param {Chart3DPolygon} splitPolygon - The polygon to split. * @param {Chart3DPolygon} refPolygon - The reference polygon for splitting. * @returns {Chart3DPolyCollections} - The resulting back and front parts. * @private */ BinaryTreeBuilder.prototype.splitPolygon = function (splitPolygon, refPolygon) { var backPoint = []; var frontPoint = []; if (splitPolygon.points != null) { var polyPoints = []; var backPartPoints = []; var frontPartPoints = []; var outputs = void 0; var inputs = void 0; var count = splitPolygon.points.length; for (var i = 0; i < count; i++) { var pointB = splitPolygon.points[i]; var pointC = splitPolygon.points[this.getNext(i + 1, count)]; var sideB = this.classifyPoint(pointB, refPolygon); var sideC = this.classifyPoint(pointC, refPolygon); var attributeB = this.vector3DIndexClassification(pointB, polyPoints.length, sideB); polyPoints.push(attributeB); if (sideB !== sideC && sideB !== 'OnPlane' && sideC !== 'OnPlane') { var vectorValue = vector.vector3DMinus(pointB, pointC); var direction = vector.vector3DMinus(vector.vector3DStarMultiply(refPolygon.normal, -refPolygon.d), pointC); var signedDistance = vector.vector3DAdd(direction, refPolygon.normal); var intersectionParameter = signedDistance / vector.vector3DAdd(refPolygon.normal, vectorValue); var intersectionPoint = vector.vector3DPlus(pointC, vector.vector3DStarMultiply(vectorValue, intersectionParameter)); var attributeIntersection = this.vector3DIndexClassification(intersectionPoint, polyPoints.length, 'OnPlane'); polyPoints.push(attributeIntersection); backPartPoints.push(attributeIntersection); frontPartPoints.push(attributeIntersection); } else if (sideB === 'OnPlane') { var pointA = splitPolygon.points[this.getNext(i - 1, count)]; var sideA = this.classifyPoint(pointA, refPolygon); if (sideA === sideC) { continue; } if (sideA !== 'OnPlane' && sideC !== 'OnPlane') { backPartPoints.push(attributeB); frontPartPoints.push(attributeB); } else if (sideA === 'OnPlane') { switch (sideC) { case 'OnBack': backPartPoints.push(attributeB); break; case 'OnFront': frontPartPoints.push(attributeB); break; } } else if (sideC === 'OnPlane') { switch (sideA) { case 'OnBack': backPartPoints.push(attributeB); break; case 'OnFront': frontPartPoints.push(attributeB); break; } } } } if (frontPartPoints.length !== 0 || backPartPoints.length !== 0) { for (var i = 0; i < backPartPoints.length - 1; i += 2) { var backAttribute1 = backPartPoints[i]; var backAttribute2 = backPartPoints[i + 1]; backAttribute1.cuttingBackPoint = true; backAttribute2.cuttingBackPoint = true; backAttribute1.alterCuttingBackPairIndex = backAttribute2.index; backAttribute2.alterCuttingBackPairIndex = backAttribute1.index; } for (var i = 0; i < frontPartPoints.length - 1; i += 2) { var frontAttribute1 = frontPartPoints[i]; var frontAttribute2 = frontPartPoints[i + 1]; frontAttribute1.cuttingFrontPoint = true; frontAttribute2.cuttingFrontPoint = true; frontAttribute1.alterCuttingFrontPairIndex = frontAttribute2.index; frontAttribute2.alterCuttingFrontPairIndex = frontAttribute1.index; } for (var i = 0; i < backPartPoints.length - 1; i++) { var backAttribute1 = backPartPoints[i]; if (backAttribute1.alreadyCutBack) { continue; } outputs = this.cutOutBackPolygon(polyPoints, backAttribute1); if (outputs.length > 2) { var polygon1 = polygonObj.polygon3D(outputs, splitPolygon); backPoint.push(__assign({}, polygon1)); } } for (var i = 0; i < frontPartPoints.length - 1; i++) { var backAttribute2 = frontPartPoints[i]; if (backAttribute2.alreadyCutFront) { continue; } inputs = this.cutOutFrontPolygon(polyPoints, backAttribute2); if (inputs.length > 2) { var polygon2 = polygonObj.polygon3D(inputs, splitPolygon); frontPoint.push(__assign({}, polygon2)); } } } } else { backPoint.push(splitPolygon); frontPoint.push(splitPolygon); } return { backPolygon: backPoint, frontPolygon: frontPoint }; }; /** * Cuts out the front part of a polygon based on the PolyAttributes. * * @param {Chart3DPolyAttributes[]} polyPoints - The PolyAttributes array of the polygon. * @param {Chart3DPolyAttributes} initialVertex - The PolyAttributes representing the cutting point. * @returns {Chart3DVector[]} - The resulting points of the front part. */ BinaryTreeBuilder.prototype.cutOutFrontPolygon = function (polyPoints, initialVertex) { var points = []; var currentVertex = initialVertex; var isVector = true; while (isVector) { currentVertex.alreadyCutFront = true; points.push(currentVertex.vector); var currentVertexPair = polyPoints[currentVertex.alterCuttingFrontPairIndex]; if (currentVertex.cuttingFrontPoint) { if (!currentVertexPair.alreadyCutFront) { currentVertex = currentVertexPair; } else { var previousVertexOnBack = polyPoints[this.getNext(currentVertex.index - 1, polyPoints.length)]; var nextVertexOnBack = polyPoints[this.getNext(currentVertex.index + 1, polyPoints.length)]; if (previousVertexOnBack.result === 'OnFront' && !previousVertexOnBack.alreadyCutFront) { currentVertex = previousVertexOnBack; } else if (nextVertexOnBack.result === 'OnFront' && !nextVertexOnBack.alreadyCutFront) { currentVertex = nextVertexOnBack; } else { return points; } } } else { var previousVertexOnBack = polyPoints[this.getNext(currentVertex.index - 1, polyPoints.length)]; var nextVertexOnBack = polyPoints[this.getNext(currentVertex.index + 1, polyPoints.length)]; if (previousVertexOnBack.result !== 'OnBack' && !previousVertexOnBack.alreadyCutFront) { currentVertex = previousVertexOnBack; } else if (nextVertexOnBack.result !== 'OnBack' && !nextVertexOnBack.alreadyCutFront) { currentVertex = nextVertexOnBack; } else { return points; } } } return null; }; /** * Cuts out the back part of a polygon based on the PolyAttributes. * * @param {Chart3DPolyAttributes[]} polyPoints - The PolyAttributes array of the polygon. * @param {Chart3DPolyAttributes} initialVertex - The PolyAttributes representing the cutting point. * @returns {Chart3DVector[]} - The resulting points of the back part. */ BinaryTreeBuilder.prototype.cutOutBackPolygon = function (polyPoints, initialVertex) { var points = []; var currentVertex = initialVertex; var isVector = true; while (isVector) { currentVertex.alreadyCutBack = true; points.push(currentVertex.vector); var currentVertexPair = polyPoints[currentVertex.alterCuttingBackPairIndex]; if (currentVertex.cuttingBackPoint) { if (!currentVertexPair.alreadyCutBack) { currentVertex = currentVertexPair; } else { var previousVertexOnBack = polyPoints[this.getNext(currentVertex.index - 1, polyPoints.length)]; var nextVertexOnBack = polyPoints[this.getNext(currentVertex.index + 1, polyPoints.length)]; if (previousVertexOnBack.result === 'OnBack' && !previousVertexOnBack.alreadyCutBack) { currentVertex = previousVertexOnBack; } else if (nextVertexOnBack.result === 'OnBack' && !nextVertexOnBack.alreadyCutBack) { currentVertex = nextVertexOnBack; } else { return points; } } } else { var previousVertexOnBack = polyPoints[this.getNext(currentVertex.index - 1, polyPoints.length)]; var nextVertexOnBack = polyPoints[this.getNext(currentVertex.index + 1, polyPoints.length)]; if (previousVertexOnBack.result !== 'OnFront' && !previousVertexOnBack.alreadyCutBack) { currentVertex = previousVertexOnBack; } else if (nextVertexOnBack.result !== 'OnFront' && !nextVertexOnBack.alreadyCutBack) { currentVertex = nextVertexOnBack; } else { return points; } } } return null; }; /** * Builds a Binary Space Partitioning from a list of polygons. * * @param {Chart3DPolygon[]} [points] - The list of polygons to build the tree from. * @returns {Chart3DBspNode} - The root node of the Binary Space Partitioning tree. */ BinaryTreeBuilder.prototype.build = function (points) { if (!points) { return this.build(this.chart.polygons); } else { var inputPolygons = points; if (inputPolygons.length < 1) { return null; } var bspNode = { back: null, front: null, plane: null }; var plane = inputPolygons[0]; bspNode.plane = plane; var polygonsToLeft = []; var polygonsToRight = []; for (var i = 1, len = inputPolygons.length; i < len; i++) { var currentPolygon = inputPolygons[i]; if (currentPolygon === plane) { continue; } var classificationResult = this.classifyPolygon(plane, currentPolygon); switch (classificationResult) { case 'OnPlane': case 'ToRight': polygonsToRight.push(currentPolygon); break; case 'ToLeft': polygonsToLeft.push(currentPolygon); break; case 'Unknown': if (currentPolygon.element && (currentPolygon.element.tag === 'line' || currentPolygon.element.tag === 'text')) { polygonsToLeft.push(currentPolygon); } else { var result = this.splitPolygon(currentPolygon, plane); for (var k = 0; k < result.backPolygon.length; k++) { result.backPolygon[k].name = result.backPolygon[k].name + '-back'; polygonsToLeft.push(result.backPolygon[k]); } for (var j = 0; j < result.frontPolygon.length; j++) { result.frontPolygon[j].name = result.frontPolygon[j].name + '-front'; polygonsToRight.push(result.frontPolygon[j]); } } break; } } if (polygonsToLeft.length > 0) { bspNode.back = this.build(polygonsToLeft); } if (polygonsToRight.length > 0) { bspNode.front = this.build(polygonsToRight); } return bspNode; } }; return BinaryTreeBuilder; }()); export { BinaryTreeBuilder }; /** * The Svg3DRenderer class provides methods for rendering SVG graphics in a 3D context. */ var Svg3DRenderer = /** @class */ (function () { function Svg3DRenderer() { } /** * Gets a Chart3DStringBuilder instance for constructing strings. * * @returns {Chart3DStringBuilder} - The StringBuilder instance. */ Svg3DRenderer.prototype.getStringBuilder = function () { var data = []; var counter = 0; return { append: function (text) { data[counter++] = text; return this; }, remove: function (i, j) { data.splice(i, j || 1); return this; }, insert: function (i, text) { data.splice(i, 0, text); return this; }, toString: function (text) { return data.join(text || ''); } }; }; /** * Parses a hex color code and returns its Red green Blue values. * * @param {string} hexColorCode - The hex color code. * @returns {Chart3DColorFormat | null} - The parsed color format (Red green Blue) or null if parsing fails. */ Svg3DRenderer.prototype.hexToValue = function (hexColorCode) { var result; var values; if (hexColorCode.indexOf('rgba(') === 0) { values = hexColorCode.slice(5, -1).split(','); return values ? { red: parseInt(values[0], 10), green: parseInt(values[1], 10), blue: parseInt(values[2], 10), alpha: parseFloat(values[3]) } : null; } else if (hexColorCode.indexOf('rgb(') === 0) { values = hexColorCode.slice(4, -1).split(','); return values ? { red: parseInt(values[0], 10), green: parseInt(values[1], 10), blue: parseInt(values[2], 10) } : null; } else { result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexColorCode); return result ? { red: parseInt(result[1], 16), green: parseInt(result[2], 16), blue: parseInt(result[3], 16) } : null; } }; /** * Converts a Chart3DColorFormat object to its corresponding color string. * * @param {Chart3DColorFormat} color - The color in Chart3DColorFormat. * @returns {string} - The color string representation. */ Svg3DRenderer.prototype.hexColor = function (color) { var redValue = color.red; var greenValue = color.green; var blueValue = color.blue; if (color.alpha) { var returnColor = "rgba(" + redValue.toString() + "," + greenValue.toString() + "," + blueValue.toString() + "," + color.alpha + ")"; return returnColor; } else { var hex_1 = [redValue.toString(16), greenValue.toString(16), blueValue.toString(16)]; hex_1.forEach(function (val, nr) { if (val.length === 1) { hex_1[nr] = '0' + val; } }); return '#' + hex_1.join('').toUpperCase(); } }; /** * Checks if a given color string is in a valid format (hex or rgba). * * @param {string} color - The color string to check. * @returns {boolean} - True if the color string is valid, otherwise false. */ Svg3DRenderer.prototype.checkColorFormat = function (color) { if (color.indexOf('rgba(') === 0 || color.indexOf('rgb(') === 0) { var rgbaValues = color.substring(color.indexOf('(') + 1, color.lastIndexOf(')')).spli