UNPKG

5.37 kBPlain TextView Raw
1import { deepMix, identity } from '@antv/util';
2import { mat4, vec4 } from 'gl-matrix';
3import { Vector3, Vector, Transform3D, Options3D, Transformation3D, Transformer3D, Matrix4, Vector4 } from './type';
4import { compose, isMatrix, extend3D } from './utils';
5import { cartesian3D, translate3D, scale3D, transpose3D } from './transforms';
6
7export class Coordinate3D {
8 // 所有变换合成后的函数
9 private output: Transform3D;
10
11 // 所有变换合成后的逆函数
12 private input: Transform3D;
13
14 // 当前的选项
15 private options: Options3D = {
16 x: 0,
17 y: 0,
18 z: 0,
19 width: 300,
20 height: 150,
21 depth: 150,
22 transformations: [],
23 };
24
25 // 当前可以使用的变换
26 private transformers = {
27 cartesian3D,
28 translate3D,
29 scale3D,
30 transpose3D,
31 };
32
33 /**
34 * Create a new Coordinate Object.
35 * @param options Custom options
36 */
37 constructor(options?: Partial<Options3D>) {
38 this.update(options);
39 }
40
41 /**
42 * Update options and inner state.
43 * @param options Options to be updated
44 */
45 public update(options: Partial<Options3D>) {
46 this.options = deepMix({}, this.options, options);
47 this.recoordinate();
48 }
49
50 /**
51 * Returns a new Coordinate with same options.
52 * @returns Coordinate
53 */
54 public clone() {
55 return new Coordinate3D(this.options);
56 }
57
58 /**
59 * Returns current options.
60 * @returns options
61 */
62 public getOptions() {
63 return this.options;
64 }
65
66 /**
67 * Clear transformations and update.
68 */
69 public clear() {
70 this.update({
71 transformations: [],
72 });
73 }
74
75 /**
76 * Returns the size of the bounding box of the coordinate.
77 * @returns [width, height, depth]
78 */
79 public getSize(): [number, number, number] {
80 const { width, height, depth } = this.options;
81 return [width, height, depth];
82 }
83
84 /**
85 * Returns the center of the bounding box of the coordinate.
86 * @returns [centerX, centerY, centerZ]
87 */
88 public getCenter(): [number, number, number] {
89 const { x, y, z, width, height, depth } = this.options;
90 return [(x * 2 + width) / 2, (y * 2 + height) / 2, (z * 2 + depth) / 2];
91 }
92
93 /**
94 * Add selected transformation.
95 * @param args transform type and params
96 * @returns Coordinate
97 */
98 public transform(...args: Transformation3D) {
99 const { transformations } = this.options;
100 this.update({
101 transformations: [...transformations, [...args]],
102 });
103 return this;
104 }
105
106 /**
107 * Apples transformations for the current vector.
108 * @param vector original vector3
109 * @returns transformed vector3
110 */
111 public map(vector: Vector3 | Vector) {
112 return this.output(vector);
113 }
114
115 /**
116 * Apples invert transformations for the current vector.
117 * @param vector transformed vector3
118 * @param vector original vector3
119 */
120 public invert(vector: Vector3 | Vector) {
121 return this.input(vector);
122 }
123
124 private recoordinate() {
125 this.output = this.compose();
126 this.input = this.compose(true);
127 }
128
129 // 将所有的变换合成一个函数
130 // 变换有两种类型:矩阵变换和函数变换
131 // 处理过程中需要把连续的矩阵变换合成一个变换函数,然后在和其他变换函数合成最终的变换函数
132 private compose(invert = false) {
133 const transformations = invert ? [...this.options.transformations].reverse() : this.options.transformations;
134 const getter = invert ? (d: Transformer3D) => d.untransform : (d: Transformer3D) => d.transform;
135 const matrixes = [];
136 const transforms = [];
137 const add = (transform: Transform3D, extended = true) =>
138 transforms.push(extended ? extend3D(transform) : transform);
139
140 for (const [name, ...args] of transformations) {
141 const createTransformer = this.transformers[name];
142 if (createTransformer) {
143 const { x, y, z, width, height, depth } = this.options;
144 const transformer = createTransformer([...args], x, y, z, width, height, depth);
145
146 if (isMatrix(transformer)) {
147 // 如果当前变换是矩阵变换,那么先保存下来
148 matrixes.push(transformer);
149 } else {
150 // 如果当前变换是函数变换,并且之前有没有合成的矩阵变换,那么现将之前的矩阵变换合成
151 if (matrixes.length) {
152 const transform = this.createMatrixTransform(matrixes, invert);
153 add(transform);
154 matrixes.splice(0, matrixes.length);
155 }
156 const transform = getter(transformer as Transformer3D) || identity;
157 add(transform, true);
158 }
159 }
160 }
161
162 // 合成剩下的矩阵变换
163 if (matrixes.length) {
164 const transform = this.createMatrixTransform(matrixes, invert);
165 add(transform);
166 }
167
168 return compose<Vector3 | Vector>(...transforms);
169 }
170
171 // 将连续的矩阵的运算合成一个变换函数
172 private createMatrixTransform(matrixes: Matrix4[], invert: boolean): Transform3D {
173 const matrix = mat4.create();
174 if (invert) matrixes.reverse();
175 matrixes.forEach((m) => mat4.mul(matrix, matrix, m));
176 if (invert) {
177 mat4.invert(matrix, mat4.clone(matrix));
178 }
179 return (vector: Vector3): Vector3 => {
180 const vector4: Vector4 = [vector[0], vector[1], vector[2], 1];
181
182 vec4.transformMat4(vector4, vector4, matrix);
183 return [vector4[0], vector4[1], vector4[2]];
184 };
185 }
186}