Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 49512db

Browse files
committed
convert library to TS, refactor Transform2d and Transform3d to functional components with shared hooks
1 parent 298f54a commit 49512db

File tree

8 files changed

+595
-0
lines changed

8 files changed

+595
-0
lines changed

‎src/Transform2d.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import PropTypes from 'prop-types';
2+
import { mat2d, vec2 } from 'gl-matrix';
3+
4+
import { vec2Obj, vec2GlMatrix, mat2dGlMatrix } from './constants';
5+
import { setVec2FromProp } from './utils';
6+
import { useFactoryRef } from './useFactoryRef';
7+
import { useRender } from './useRender';
8+
import type {
9+
MultiplicationOrder,
10+
TransformChildren,
11+
Vec2Object,
12+
} from './types';
13+
14+
const propTypes = {
15+
parentMatrixWorld: mat2dGlMatrix,
16+
multiplicationOrder: PropTypes.oneOf(['PRE', 'POST']),
17+
translate: PropTypes.oneOfType([vec2GlMatrix, vec2Obj]),
18+
scale: PropTypes.oneOfType([vec2GlMatrix, vec2Obj, PropTypes.number]),
19+
rotate: PropTypes.number,
20+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
21+
};
22+
23+
export type Transform2dProps = {
24+
children: TransformChildren<mat2d>;
25+
parentMatrixWorld?: mat2d;
26+
multiplicationOrder?: MultiplicationOrder;
27+
translate?: vec2 | Vec2Object;
28+
scale?: vec2 | Vec2Object | number;
29+
rotate?: number;
30+
};
31+
32+
export type Apply2dTransformsParams = {
33+
parentMatrixWorld: mat2d;
34+
matrix: mat2d;
35+
matrixWorld: mat2d;
36+
vTranslation: vec2;
37+
vScale: vec2;
38+
multiplicationOrder: MultiplicationOrder;
39+
translate?: vec2 | Vec2Object;
40+
scale?: vec2 | Vec2Object | number;
41+
rotate?: number;
42+
};
43+
44+
// TODO: somehow implement safe memoization with some sort of equality check?
45+
// considering mutable objects
46+
export function apply2dTransforms({
47+
parentMatrixWorld,
48+
matrix,
49+
matrixWorld,
50+
vTranslation,
51+
vScale,
52+
multiplicationOrder,
53+
translate,
54+
scale,
55+
rotate,
56+
}: Apply2dTransformsParams) {
57+
const theta = typeof rotate === 'number' ? rotate : 0;
58+
59+
mat2d.identity(matrix);
60+
61+
setVec2FromProp(vTranslation, translate);
62+
setVec2FromProp(vScale, scale, 1);
63+
64+
// T * R * S
65+
mat2d.translate(matrix, matrix, vTranslation);
66+
mat2d.rotate(matrix, matrix, theta);
67+
mat2d.scale(matrix, matrix, vScale);
68+
69+
if (multiplicationOrder === 'PRE') {
70+
mat2d.multiply(matrixWorld, matrix, parentMatrixWorld);
71+
} else {
72+
mat2d.multiply(matrixWorld, parentMatrixWorld, matrix);
73+
}
74+
}
75+
76+
const Transform2d = ({
77+
children,
78+
parentMatrixWorld,
79+
translate,
80+
scale,
81+
rotate,
82+
multiplicationOrder = 'POST',
83+
}: Transform2dProps) => {
84+
const safeParentMatrixWorld = useFactoryRef<mat2d>(
85+
() => parentMatrixWorld || mat2d.create(),
86+
);
87+
const matrix = useFactoryRef<mat2d>(() => mat2d.create());
88+
const matrixWorld = useFactoryRef<mat2d>(() => mat2d.create());
89+
const vTranslation = useFactoryRef<vec2>(() => vec2.create());
90+
const vScale = useFactoryRef<vec2>(() => vec2.create());
91+
92+
apply2dTransforms({
93+
parentMatrixWorld: safeParentMatrixWorld.current,
94+
matrix: matrix.current,
95+
matrixWorld: matrixWorld.current,
96+
vTranslation: vTranslation.current,
97+
vScale: vScale.current,
98+
multiplicationOrder,
99+
translate,
100+
scale,
101+
rotate,
102+
});
103+
104+
const render = useRender<mat2d>({
105+
cssMatrixPrefix: 'matrix',
106+
matrixWorld,
107+
multiplicationOrder,
108+
});
109+
110+
return render(children);
111+
};
112+
113+
Transform2d.propTypes = propTypes;
114+
115+
export default Transform2d;

‎src/Transform3d.tsx

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import PropTypes from 'prop-types';
2+
import { mat4, vec3 } from 'gl-matrix';
3+
4+
import { vec3Obj, vec3GlMatrix, mat4GlMatrix } from './constants';
5+
import { setVec3FromProp } from './utils';
6+
import { useFactoryRef } from './useFactoryRef';
7+
import { useRender } from './useRender';
8+
import type {
9+
MultiplicationOrder,
10+
TransformChildren,
11+
Vec3Object,
12+
} from './types';
13+
14+
const propTypes = {
15+
parentMatrixWorld: mat4GlMatrix,
16+
multiplicationOrder: PropTypes.oneOf(['PRE', 'POST']),
17+
translate: PropTypes.oneOfType([vec3GlMatrix, vec3Obj]),
18+
scale: PropTypes.oneOfType([vec3GlMatrix, vec3Obj, PropTypes.number]),
19+
rotate: PropTypes.number,
20+
rotateAxis: PropTypes.oneOfType([vec3GlMatrix, vec3Obj]),
21+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
22+
};
23+
24+
export type Transform3dProps = {
25+
children: TransformChildren<mat4>;
26+
parentMatrixWorld?: mat4;
27+
multiplicationOrder?: MultiplicationOrder;
28+
translate?: vec3 | Vec3Object;
29+
scale?: vec3 | Vec3Object | number;
30+
rotate?: number;
31+
rotateAxis?: vec3 | Vec3Object;
32+
};
33+
34+
export type Apply3dTransformsParams = {
35+
parentMatrixWorld: mat4;
36+
matrix: mat4;
37+
matrixWorld: mat4;
38+
vTranslation: vec3;
39+
vScale: vec3;
40+
vRotationAxis: vec3;
41+
multiplicationOrder: MultiplicationOrder;
42+
translate?: vec3 | Vec3Object;
43+
scale?: vec3 | Vec3Object | number;
44+
rotate?: number;
45+
rotateAxis?: vec3 | Vec3Object;
46+
};
47+
48+
// TODO: somehow implement safe memoization with some sort of equality check?
49+
// considering mutable objects
50+
export function apply3dTransforms({
51+
parentMatrixWorld,
52+
matrix,
53+
matrixWorld,
54+
vTranslation,
55+
vScale,
56+
vRotationAxis,
57+
multiplicationOrder,
58+
translate,
59+
scale,
60+
rotate,
61+
rotateAxis,
62+
}: Apply3dTransformsParams) {
63+
const theta = typeof rotate === 'number' ? rotate : 0;
64+
65+
mat4.identity(matrix);
66+
67+
setVec3FromProp(vTranslation, translate);
68+
setVec3FromProp(vScale, scale, 1, 1, 1);
69+
setVec3FromProp(vRotationAxis, rotateAxis, 0, 0, 1);
70+
71+
// T * R * S
72+
mat4.translate(matrix, matrix, vTranslation);
73+
mat4.rotate(matrix, matrix, theta, vRotationAxis);
74+
mat4.scale(matrix, matrix, vScale);
75+
76+
if (multiplicationOrder === 'PRE') {
77+
mat4.multiply(matrixWorld, matrix, parentMatrixWorld);
78+
} else {
79+
mat4.multiply(matrixWorld, parentMatrixWorld, matrix);
80+
}
81+
}
82+
83+
const Transform3d = ({
84+
children,
85+
parentMatrixWorld,
86+
translate,
87+
scale,
88+
rotate,
89+
rotateAxis,
90+
multiplicationOrder = 'POST',
91+
}: Transform3dProps) => {
92+
const safeParentMatrixWorld = useFactoryRef<mat4>(
93+
() => parentMatrixWorld || mat4.create(),
94+
);
95+
const matrix = useFactoryRef<mat4>(() => mat4.create());
96+
const matrixWorld = useFactoryRef<mat4>(() => mat4.create());
97+
const vTranslation = useFactoryRef<vec3>(() => vec3.create());
98+
const vScale = useFactoryRef<vec3>(() => vec3.create());
99+
const vRotationAxis = useFactoryRef<vec3>(() => vec3.create());
100+
101+
apply3dTransforms({
102+
parentMatrixWorld: safeParentMatrixWorld.current,
103+
matrix: matrix.current,
104+
matrixWorld: matrixWorld.current,
105+
vTranslation: vTranslation.current,
106+
vScale: vScale.current,
107+
vRotationAxis: vRotationAxis.current,
108+
multiplicationOrder,
109+
translate,
110+
scale,
111+
rotate,
112+
rotateAxis,
113+
});
114+
115+
const render = useRender<mat4>({
116+
cssMatrixPrefix: 'matrix3d',
117+
matrixWorld,
118+
multiplicationOrder,
119+
});
120+
121+
return render(children);
122+
};
123+
124+
Transform3d.propTypes = propTypes;
125+
126+
export default Transform3d;

‎src/constants.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import {
2+
isVec2,
3+
isVec3,
4+
isMat2d,
5+
isMat4,
6+
isVec2Object,
7+
isVec3Object,
8+
} from './utils';
9+
10+
type PropTypes = { [key: string]: any };
11+
12+
export const vec2Obj = <P extends PropTypes>(
13+
props: P,
14+
propName: string & keyof P,
15+
componentName: string,
16+
) => {
17+
const passes = isVec2Object(props[propName]);
18+
return passes
19+
? null
20+
: new Error(`${propName} in ${componentName} is not a vec2 shape`);
21+
};
22+
23+
export const vec3Obj = <P extends PropTypes>(
24+
props: P,
25+
propName: string & keyof P,
26+
componentName: string,
27+
) => {
28+
const passes = isVec3Object(props[propName]);
29+
return passes
30+
? null
31+
: new Error(`${propName} in ${componentName} is not a vec3 shape`);
32+
};
33+
34+
export const vec2GlMatrix = <P extends PropTypes>(
35+
props: P,
36+
propName: string & keyof P,
37+
componentName: string,
38+
) => {
39+
const passes = isVec2(props[propName]);
40+
return passes
41+
? null
42+
: new Error(`${propName} in ${componentName} is not a gl-matrix vec2`);
43+
};
44+
45+
export const vec3GlMatrix = <P extends PropTypes>(
46+
props: P,
47+
propName: string & keyof P,
48+
componentName: string,
49+
) => {
50+
const passes = isVec3(props[propName]);
51+
return passes
52+
? null
53+
: new Error(`${propName} in ${componentName} is not a gl-matrix vec3`);
54+
};
55+
56+
export const mat2dGlMatrix = <P extends PropTypes>(
57+
props: P,
58+
propName: string & keyof P,
59+
componentName: string,
60+
) => {
61+
const passes = isMat2d(props[propName]);
62+
return passes
63+
? null
64+
: new Error(`${propName} in ${componentName} is not a gl-matrix mat2d`);
65+
};
66+
67+
export const mat4GlMatrix = <P extends PropTypes>(
68+
props: P,
69+
propName: string & keyof P,
70+
componentName: string,
71+
) => {
72+
const passes = isMat4(props[propName]);
73+
return passes
74+
? null
75+
: new Error(`${propName} in ${componentName} is not a gl-matrix mat4`);
76+
};

‎src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export {
2+
vec2Obj,
3+
vec3Obj,
4+
vec2GlMatrix,
5+
vec3GlMatrix,
6+
mat2dGlMatrix,
7+
mat4GlMatrix,
8+
} from './constants';
9+
export { default as Transform2d } from './Transform2d';
10+
export { default as Transform3d } from './Transform3d';

‎src/types.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import type React from 'react';
2+
import type { vec2, vec3 } from 'gl-matrix';
3+
4+
export type GLMatrixType = Array<number> | Float32Array;
5+
export type Vec2Object = {
6+
x?: number;
7+
y?: number;
8+
};
9+
export type Vec3Object = Vec2Object & {
10+
z?: number;
11+
};
12+
export type Vec2SettableProperty = number | vec2 | Vec2Object;
13+
export type Vec3SettableProperty = number | vec3 | Vec3Object;
14+
15+
/**
16+
* POST is the default, more natural mathematically and (arguably) more useful, it behaves in the same order as when
17+
* applying multiple transforms on the css `transform` property:
18+
*
19+
* <Transform2d translate={{x:100, y:100}} multiplicationOrder={Transform2d.MULTIPLICATION_ORDER.POST}>
20+
* <Transform2d rotate={0.7854}>
21+
* <Transform2d scale={2}>
22+
* <div></div>
23+
* </Transform2d>
24+
* </Transform>
25+
* </Transform2d>
26+
*
27+
* Is the same as applying a style of `transform: translate(100px,100px) rotate(0.7845rad) scale(2)` to the inner div
28+
*
29+
* PRE mimics how CSS transforms work between nested DOM elements
30+
*
31+
* <Transform2d translate={{x:100, y:100}} multiplicationOrder={Transform2d.MULTIPLICATION_ORDER.PRE}>
32+
* <Transform2d rotate={0.7854}>
33+
* <Transform2d scale={2}>
34+
* <div></div>
35+
* </Transform2d>
36+
* </Transform>
37+
* </Transform2d>
38+
*
39+
* Is basically the same (assuming all the nested divs are positioned absolutely in the same place) as:
40+
*
41+
* <div style="transform: translate(100px, 100px)>
42+
* <div style="transform: rotate(0.7854rad)">
43+
* <div style="transform: scale(2)">
44+
* <div></div>
45+
* </div>
46+
* </div>
47+
* </div>
48+
*/
49+
export type MultiplicationOrder = 'PRE' | 'POST';
50+
51+
export type CSSMatrixPrefix = 'matrix' | 'matrix3d';
52+
53+
export type TransformChildProps<Matrix extends GLMatrixType> = {
54+
parentMatrixWorld?: Matrix;
55+
multiplicationOrder?: MultiplicationOrder;
56+
style?: React.CSSProperties;
57+
};
58+
59+
export type TransformChildFunction<Matrix extends GLMatrixType> = (
60+
params: TransformChildProps<Matrix>,
61+
) => JSX.Element;
62+
63+
export type TransformChildren<Matrix extends GLMatrixType> =
64+
| React.ReactElement
65+
| React.ReactElement[]
66+
| TransformChildFunction<Matrix>;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /