1 | import { deepMix, identity } from '@antv/util';
|
2 | import { mat3, vec3 } from 'gl-matrix';
|
3 | import { Options, Transformation, Transform, Transformer, Matrix3, Vector3, Vector2, Vector } from './type';
|
4 | import { compose, isMatrix, extend } from './utils';
|
5 | import {
|
6 | cartesian,
|
7 | translate,
|
8 | custom,
|
9 | matrix,
|
10 | polar,
|
11 | transpose,
|
12 | scale,
|
13 | shearX,
|
14 | shearY,
|
15 | reflect,
|
16 | reflectX,
|
17 | reflectY,
|
18 | rotate,
|
19 | helix,
|
20 | parallel,
|
21 | fisheye,
|
22 | fisheyeX,
|
23 | fisheyeY,
|
24 | fisheyeCircular,
|
25 | } from './transforms';
|
26 |
|
27 | export class Coordinate {
|
28 |
|
29 | private output: Transform;
|
30 |
|
31 |
|
32 | private input: Transform;
|
33 |
|
34 |
|
35 | private options: Options = {
|
36 | x: 0,
|
37 | y: 0,
|
38 | width: 300,
|
39 | height: 150,
|
40 | transformations: [],
|
41 | };
|
42 |
|
43 |
|
44 | private transformers = {
|
45 | cartesian,
|
46 | translate,
|
47 | custom,
|
48 | matrix,
|
49 | polar,
|
50 | transpose,
|
51 | scale,
|
52 | 'shear.x': shearX,
|
53 | 'shear.y': shearY,
|
54 | reflect,
|
55 | 'reflect.x': reflectX,
|
56 | 'reflect.y': reflectY,
|
57 | rotate,
|
58 | helix,
|
59 | parallel,
|
60 | fisheye,
|
61 | 'fisheye.x': fisheyeX,
|
62 | 'fisheye.y': fisheyeY,
|
63 | 'fisheye.circular': fisheyeCircular,
|
64 | };
|
65 |
|
66 | |
67 |
|
68 |
|
69 |
|
70 | constructor(options?: Partial<Options>) {
|
71 | this.update(options);
|
72 | }
|
73 |
|
74 | |
75 |
|
76 |
|
77 |
|
78 | public update(options: Partial<Options>) {
|
79 | this.options = deepMix({}, this.options, options);
|
80 | this.recoordinate();
|
81 | }
|
82 |
|
83 | |
84 |
|
85 |
|
86 |
|
87 | public clone() {
|
88 | return new Coordinate(this.options);
|
89 | }
|
90 |
|
91 | |
92 |
|
93 |
|
94 |
|
95 | public getOptions() {
|
96 | return this.options;
|
97 | }
|
98 |
|
99 | |
100 |
|
101 |
|
102 | public clear() {
|
103 | this.update({
|
104 | transformations: [],
|
105 | });
|
106 | }
|
107 |
|
108 | |
109 |
|
110 |
|
111 |
|
112 | public getSize(): [number, number] {
|
113 | const { width, height } = this.options;
|
114 | return [width, height];
|
115 | }
|
116 |
|
117 | |
118 |
|
119 |
|
120 |
|
121 | public getCenter(): [number, number] {
|
122 | const { x, y, width, height } = this.options;
|
123 | return [(x * 2 + width) / 2, (y * 2 + height) / 2];
|
124 | }
|
125 |
|
126 | |
127 |
|
128 |
|
129 |
|
130 |
|
131 | public transform(...args: Transformation) {
|
132 | const { transformations } = this.options;
|
133 | this.update({
|
134 | transformations: [...transformations, [...args]],
|
135 | });
|
136 | return this;
|
137 | }
|
138 |
|
139 | |
140 |
|
141 |
|
142 |
|
143 |
|
144 | public map(vector: Vector2 | Vector) {
|
145 | return this.output(vector);
|
146 | }
|
147 |
|
148 | |
149 |
|
150 |
|
151 |
|
152 |
|
153 | public invert(vector: Vector2 | Vector) {
|
154 | return this.input(vector);
|
155 | }
|
156 |
|
157 | private recoordinate() {
|
158 | this.output = this.compose();
|
159 | this.input = this.compose(true);
|
160 | }
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | private compose(invert = false) {
|
166 | const transformations = invert ? [...this.options.transformations].reverse() : this.options.transformations;
|
167 | const getter = invert ? (d: Transformer) => d.untransform : (d: Transformer) => d.transform;
|
168 | const matrixes = [];
|
169 | const transforms = [];
|
170 | const add = (transform: Transform, extended = true) => transforms.push(extended ? extend(transform) : transform);
|
171 |
|
172 | for (const [name, ...args] of transformations) {
|
173 | const createTransformer = this.transformers[name];
|
174 | if (createTransformer) {
|
175 | const { x, y, width, height } = this.options;
|
176 | const transformer = createTransformer([...args], x, y, width, height);
|
177 | if (isMatrix(transformer)) {
|
178 |
|
179 | matrixes.push(transformer);
|
180 | } else {
|
181 |
|
182 | if (matrixes.length) {
|
183 | const transform = this.createMatrixTransform(matrixes, invert);
|
184 | add(transform);
|
185 | matrixes.splice(0, matrixes.length);
|
186 | }
|
187 | const transform = getter(transformer) || identity;
|
188 | add(transform, name !== 'parallel');
|
189 | }
|
190 | }
|
191 | }
|
192 |
|
193 |
|
194 | if (matrixes.length) {
|
195 | const transform = this.createMatrixTransform(matrixes, invert);
|
196 | add(transform);
|
197 | }
|
198 |
|
199 | return compose<Vector2 | Vector>(...transforms);
|
200 | }
|
201 |
|
202 |
|
203 | private createMatrixTransform(matrixes: Matrix3[], invert: boolean): Transform {
|
204 | const matrix = mat3.create();
|
205 | if (invert) matrixes.reverse();
|
206 | matrixes.forEach((m) => mat3.mul(matrix, matrix, m));
|
207 | if (invert) {
|
208 | mat3.invert(matrix, mat3.clone(matrix));
|
209 | }
|
210 | return (vector: Vector2): Vector2 => {
|
211 | const vector3: Vector3 = [vector[0], vector[1], 1];
|
212 | vec3.transformMat3(vector3, vector3, matrix);
|
213 | return [vector3[0], vector3[1]];
|
214 | };
|
215 | }
|
216 | }
|