1 | import { deepMix, identity } from '@antv/util';
|
2 | import { mat4, vec4 } from 'gl-matrix';
|
3 | import { Vector3, Vector, Transform3D, Options3D, Transformation3D, Transformer3D, Matrix4, Vector4 } from './type';
|
4 | import { compose, isMatrix, extend3D } from './utils';
|
5 | import { cartesian3D, translate3D, scale3D, transpose3D } from './transforms';
|
6 |
|
7 | export 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 |
|
35 |
|
36 |
|
37 | constructor(options?: Partial<Options3D>) {
|
38 | this.update(options);
|
39 | }
|
40 |
|
41 | |
42 |
|
43 |
|
44 |
|
45 | public update(options: Partial<Options3D>) {
|
46 | this.options = deepMix({}, this.options, options);
|
47 | this.recoordinate();
|
48 | }
|
49 |
|
50 | |
51 |
|
52 |
|
53 |
|
54 | public clone() {
|
55 | return new Coordinate3D(this.options);
|
56 | }
|
57 |
|
58 | |
59 |
|
60 |
|
61 |
|
62 | public getOptions() {
|
63 | return this.options;
|
64 | }
|
65 |
|
66 | |
67 |
|
68 |
|
69 | public clear() {
|
70 | this.update({
|
71 | transformations: [],
|
72 | });
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 | public getSize(): [number, number, number] {
|
80 | const { width, height, depth } = this.options;
|
81 | return [width, height, depth];
|
82 | }
|
83 |
|
84 | |
85 |
|
86 |
|
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 |
|
95 |
|
96 |
|
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 |
|
108 |
|
109 |
|
110 |
|
111 | public map(vector: Vector3 | Vector) {
|
112 | return this.output(vector);
|
113 | }
|
114 |
|
115 | |
116 |
|
117 |
|
118 |
|
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 | }
|