@saboit-dev/thomas 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +138 -0
- package/index.cjs.js +1 -0
- package/index.d.ts +2 -0
- package/index.js +6 -0
- package/package.json +54 -0
- package/text_engine_2d/MSDFTextGeometry/TextLayout.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/TextLayout.d.ts +48 -0
- package/text_engine_2d/MSDFTextGeometry/TextLayout.js +357 -0
- package/text_engine_2d/MSDFTextGeometry/index.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/index.d.ts +51 -0
- package/text_engine_2d/MSDFTextGeometry/index.js +159 -0
- package/text_engine_2d/MSDFTextGeometry/utils.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/utils.d.ts +3 -0
- package/text_engine_2d/MSDFTextGeometry/utils.js +46 -0
- package/text_engine_2d/MSDFTextGeometry/vertices.cjs.js +1 -0
- package/text_engine_2d/MSDFTextGeometry/vertices.d.ts +28 -0
- package/text_engine_2d/MSDFTextGeometry/vertices.js +84 -0
- package/text_engine_2d/MSDFTextMaterial/fragment.glsl.js +3 -0
- package/text_engine_2d/MSDFTextMaterial/index.cjs.js +1 -0
- package/text_engine_2d/MSDFTextMaterial/index.d.ts +4 -0
- package/text_engine_2d/MSDFTextMaterial/index.js +38 -0
- package/text_engine_2d/MSDFTextMaterial/vertex.glsl.js +3 -0
- package/text_engine_2d/index.cjs.js +1 -0
- package/text_engine_2d/index.d.ts +6 -0
- package/text_engine_2d/index.js +18 -0
- package/text_engine_2d/react/SDFText.cjs.js +1 -0
- package/text_engine_2d/react/SDFText.d.ts +9 -0
- package/text_engine_2d/react/SDFText.js +63 -0
- package/text_engine_2d/react/SDFTextProvider.cjs.js +1 -0
- package/text_engine_2d/react/SDFTextProvider.d.ts +6 -0
- package/text_engine_2d/react/SDFTextProvider.js +191 -0
- package/text_engine_2d/react/context.cjs.js +1 -0
- package/text_engine_2d/react/context.d.ts +3 -0
- package/text_engine_2d/react/context.js +5 -0
- package/text_engine_2d/react/types.cjs.js +1 -0
- package/text_engine_2d/react/types.d.ts +33 -0
- package/text_engine_2d/react/types.js +1 -0
- package/text_engine_2d/react/utils.cjs.js +1 -0
- package/text_engine_2d/react/utils.d.ts +2 -0
- package/text_engine_2d/react/utils.js +6 -0
- package/text_engine_2d/register.cjs.js +1 -0
- package/text_engine_2d/register.d.ts +12 -0
- package/text_engine_2d/register.js +12 -0
- package/text_engine_3d/InstancedText.cjs.js +1 -0
- package/text_engine_3d/InstancedText.d.ts +13 -0
- package/text_engine_3d/InstancedText.js +127 -0
- package/text_engine_3d/InstancedTextPlaceholder.cjs.js +1 -0
- package/text_engine_3d/InstancedTextPlaceholder.d.ts +23 -0
- package/text_engine_3d/InstancedTextPlaceholder.js +50 -0
- package/text_engine_3d/InstancedTextProvider.cjs.js +1 -0
- package/text_engine_3d/InstancedTextProvider.d.ts +15 -0
- package/text_engine_3d/InstancedTextProvider.js +124 -0
- package/text_engine_3d/index.cjs.js +1 -0
- package/text_engine_3d/index.d.ts +3 -0
- package/text_engine_3d/index.js +11 -0
- package/text_engine_3d/instancedTextBuffers.cjs.js +1 -0
- package/text_engine_3d/instancedTextBuffers.d.ts +28 -0
- package/text_engine_3d/instancedTextBuffers.js +105 -0
- package/text_engine_3d/types.cjs.js +1 -0
- package/text_engine_3d/types.d.ts +37 -0
- package/text_engine_3d/types.js +8 -0
- package/text_engine_3d/useInstancedTextMaterial.cjs.js +1 -0
- package/text_engine_3d/useInstancedTextMaterial.d.ts +4 -0
- package/text_engine_3d/useInstancedTextMaterial.js +73 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
function generateAttributes(glyphs, texWidth, texHeight, flipY) {
|
|
2
|
+
const uvs = new Float32Array(glyphs.length * 4 * 2);
|
|
3
|
+
const positions = new Float32Array(glyphs.length * 4 * 3);
|
|
4
|
+
const centers = new Float32Array(glyphs.length * 4 * 2);
|
|
5
|
+
let i = 0;
|
|
6
|
+
let j = 0;
|
|
7
|
+
let k = 0;
|
|
8
|
+
glyphs.forEach(function (glyph) {
|
|
9
|
+
const bitmap = glyph.data;
|
|
10
|
+
|
|
11
|
+
// UV
|
|
12
|
+
const bw = bitmap.x + bitmap.width;
|
|
13
|
+
const bh = bitmap.y + bitmap.height;
|
|
14
|
+
|
|
15
|
+
// top left position
|
|
16
|
+
const u0 = bitmap.x / texWidth;
|
|
17
|
+
let v1 = bitmap.y / texHeight;
|
|
18
|
+
const u1 = bw / texWidth;
|
|
19
|
+
let v0 = bh / texHeight;
|
|
20
|
+
if (flipY) {
|
|
21
|
+
v1 = (texHeight - bitmap.y) / texHeight;
|
|
22
|
+
v0 = (texHeight - bh) / texHeight;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// BL
|
|
26
|
+
uvs[i++] = u0;
|
|
27
|
+
uvs[i++] = v1;
|
|
28
|
+
// TL
|
|
29
|
+
uvs[i++] = u0;
|
|
30
|
+
uvs[i++] = v0;
|
|
31
|
+
// TR
|
|
32
|
+
uvs[i++] = u1;
|
|
33
|
+
uvs[i++] = v0;
|
|
34
|
+
// BR
|
|
35
|
+
uvs[i++] = u1;
|
|
36
|
+
uvs[i++] = v1;
|
|
37
|
+
|
|
38
|
+
/// Positions, Centers
|
|
39
|
+
|
|
40
|
+
// bottom left position
|
|
41
|
+
const x = glyph.position[0] + bitmap.xoffset;
|
|
42
|
+
const y = glyph.position[1] + bitmap.yoffset;
|
|
43
|
+
|
|
44
|
+
// quad size
|
|
45
|
+
const w = bitmap.width;
|
|
46
|
+
const h = bitmap.height;
|
|
47
|
+
|
|
48
|
+
// Position
|
|
49
|
+
|
|
50
|
+
// BL
|
|
51
|
+
positions[j++] = x;
|
|
52
|
+
positions[j++] = y;
|
|
53
|
+
positions[j++] = 0;
|
|
54
|
+
// TL
|
|
55
|
+
positions[j++] = x;
|
|
56
|
+
positions[j++] = y + h;
|
|
57
|
+
positions[j++] = 0;
|
|
58
|
+
// TR
|
|
59
|
+
positions[j++] = x + w;
|
|
60
|
+
positions[j++] = y + h;
|
|
61
|
+
positions[j++] = 0;
|
|
62
|
+
// BR
|
|
63
|
+
positions[j++] = x + w;
|
|
64
|
+
positions[j++] = y;
|
|
65
|
+
positions[j++] = 0;
|
|
66
|
+
|
|
67
|
+
// Center
|
|
68
|
+
centers[k++] = x + w / 2;
|
|
69
|
+
centers[k++] = y + h / 2;
|
|
70
|
+
centers[k++] = x + w / 2;
|
|
71
|
+
centers[k++] = y + h / 2;
|
|
72
|
+
centers[k++] = x + w / 2;
|
|
73
|
+
centers[k++] = y + h / 2;
|
|
74
|
+
centers[k++] = x + w / 2;
|
|
75
|
+
centers[k++] = y + h / 2;
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
uvs,
|
|
79
|
+
positions,
|
|
80
|
+
centers
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { generateAttributes };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
var fragmentShader = "#define GLSLIFY 1\nvarying vec2 vUv;uniform float uOpacity;uniform float uOffset;uniform vec3 uColor;uniform sampler2D uMap;float median(float r,float g,float b){return max(min(r,g),min(max(r,g),b));}void main(){vec3 s=texture2D(uMap,vUv).rgb;float sigDist=median(s.r,s.g,s.b)-0.5+uOffset;float alpha=clamp(sigDist/fwidth(sigDist)+0.5,0.0,1.0);if(alpha<0.01)discard;gl_FragColor=vec4(uColor,uOpacity*alpha);}"; // eslint-disable-line
|
|
2
|
+
|
|
3
|
+
export { fragmentShader as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var i=require("three");class e extends i.ShaderMaterial{constructor(e={}){super(e={...{side:i.FrontSide,transparent:!0,extensions:{derivatives:!0},uniforms:{uOpacity:{value:1},uOffset:{value:0},uColor:{value:new i.Color("#ffffff")},uMap:{value:null}},vertexShader:"#define GLSLIFY 1\nvarying vec2 vUv;void main(){vec4 mvPosition=vec4(position,1.0);mvPosition=modelViewMatrix*mvPosition;gl_Position=projectionMatrix*mvPosition;vUv=uv;}",fragmentShader:"#define GLSLIFY 1\nvarying vec2 vUv;uniform float uOpacity;uniform float uOffset;uniform vec3 uColor;uniform sampler2D uMap;float median(float r,float g,float b){return max(min(r,g),min(max(r,g),b));}void main(){vec3 s=texture2D(uMap,vUv).rgb;float sigDist=median(s.r,s.g,s.b)-0.5+uOffset;float alpha=clamp(sigDist/fwidth(sigDist)+0.5,0.0,1.0);if(alpha<0.01)discard;gl_FragColor=vec4(uColor,uOpacity*alpha);}"},...e})}}module.exports=e;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ShaderMaterial, FrontSide, Color } from 'three';
|
|
2
|
+
import fragmentShader from './fragment.glsl.js';
|
|
3
|
+
import vertexShader from './vertex.glsl.js';
|
|
4
|
+
|
|
5
|
+
class MSDFTextMaterial extends ShaderMaterial {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
const defaultOptions = {
|
|
8
|
+
side: FrontSide,
|
|
9
|
+
transparent: true,
|
|
10
|
+
extensions: {
|
|
11
|
+
derivatives: true
|
|
12
|
+
},
|
|
13
|
+
uniforms: {
|
|
14
|
+
uOpacity: {
|
|
15
|
+
value: 1
|
|
16
|
+
},
|
|
17
|
+
uOffset: {
|
|
18
|
+
value: 0
|
|
19
|
+
},
|
|
20
|
+
uColor: {
|
|
21
|
+
value: new Color('#ffffff')
|
|
22
|
+
},
|
|
23
|
+
uMap: {
|
|
24
|
+
value: null
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
vertexShader,
|
|
28
|
+
fragmentShader
|
|
29
|
+
};
|
|
30
|
+
options = {
|
|
31
|
+
...defaultOptions,
|
|
32
|
+
...options
|
|
33
|
+
};
|
|
34
|
+
super(options);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { MSDFTextMaterial as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("./MSDFTextGeometry/index.cjs.js"),r=require("./MSDFTextMaterial/index.cjs.js"),t=require("./react/SDFText.cjs.js"),s=require("./react/SDFTextProvider.cjs.js");require("quad-indices"),require("three"),require("./MSDFTextGeometry/TextLayout.cjs.js"),require("word-wrapper"),require("./MSDFTextGeometry/utils.cjs.js"),require("./MSDFTextGeometry/vertices.cjs.js"),require("react"),require("./react/context.cjs.js"),require("./react/utils.cjs.js"),require("@babel/runtime/helpers/extends"),require("@react-three/drei"),require("@react-three/fiber"),require("three/examples/jsm/loaders/FontLoader"),require("./register.cjs.js"),exports.MSDFTextGeometry=e,exports.MSDFTextMaterial=r,exports.SDFText=t.SDFText,exports.SDFTextProvider=s.SDFTextProvider;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import MSDFTextGeometry from './MSDFTextGeometry';
|
|
2
|
+
import MSDFTextMaterial from './MSDFTextMaterial';
|
|
3
|
+
export { MSDFTextMaterial, MSDFTextGeometry };
|
|
4
|
+
export { SDFText } from './react/SDFText';
|
|
5
|
+
export type { SDFTextRefType } from './react/SDFText';
|
|
6
|
+
export { SDFTextProvider } from './react/SDFTextProvider';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { default as MSDFTextGeometry } from './MSDFTextGeometry/index.js';
|
|
2
|
+
export { default as MSDFTextMaterial } from './MSDFTextMaterial/index.js';
|
|
3
|
+
export { SDFText } from './react/SDFText.js';
|
|
4
|
+
export { SDFTextProvider } from './react/SDFTextProvider.js';
|
|
5
|
+
import 'quad-indices';
|
|
6
|
+
import 'three';
|
|
7
|
+
import './MSDFTextGeometry/TextLayout.js';
|
|
8
|
+
import 'word-wrapper';
|
|
9
|
+
import './MSDFTextGeometry/utils.js';
|
|
10
|
+
import './MSDFTextGeometry/vertices.js';
|
|
11
|
+
import 'react';
|
|
12
|
+
import './react/context.js';
|
|
13
|
+
import './react/utils.js';
|
|
14
|
+
import '@babel/runtime/helpers/esm/extends';
|
|
15
|
+
import '@react-three/drei';
|
|
16
|
+
import '@react-three/fiber';
|
|
17
|
+
import 'three/examples/jsm/loaders/FontLoader';
|
|
18
|
+
import './register.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("./context.cjs.js"),r=require("./utils.cjs.js");function n(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var o=n(e);const i=e.forwardRef((function({instanceKey:n,bold:i,color:u,opacity:c,outlineWidth:l,outlineOpacity:a,outlineColor:s,text:f,alignX:d,alignY:p,letterSpacing:y,yShift:g,width:v,lineHeight:h,onBeforeUpdate:j},x){const b=e.useContext(t.SDFTextProviderContext);if(!b)throw new Error("No SDFTextProvider");const O=e.useRef(null),S=e.useRef(null);return e.useImperativeHandle(x,(()=>({setTransform(e){S.current&&(S.current.transform=e)}})),[]),e.useLayoutEffect((()=>{var e;const t={bold:i,color:u,opacity:c,outlineWidth:l,outlineOpacity:a,outlineColor:s},o={text:f,alignX:d,alignY:p,letterSpacing:y,yShift:g,width:v,lineHeight:h,onBeforeUpdate:j},x=r.makeStyleId(t);return S.current=b.insertInstance(x,t,n,o,null!==(e=O.current)&&void 0!==e?e:void 0),()=>b.removeInstance(x,n)}),[b,n,f,d,p,y,g,v,h,i,u,c,l,a,s,j]),o.createElement("group",{ref:O})}));exports.SDFText=i;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Matrix4 } from 'three';
|
|
3
|
+
import { ISDFTextConfig, ISDFTextStyle } from './types';
|
|
4
|
+
export type SDFTextRefType = {
|
|
5
|
+
setTransform(transform: Matrix4): void;
|
|
6
|
+
};
|
|
7
|
+
export declare const SDFText: React.ForwardRefExoticComponent<{
|
|
8
|
+
instanceKey: string;
|
|
9
|
+
} & ISDFTextConfig & ISDFTextStyle & React.RefAttributes<SDFTextRefType>>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { forwardRef, useContext, useRef, useImperativeHandle, useLayoutEffect } from 'react';
|
|
3
|
+
import { SDFTextProviderContext } from './context.js';
|
|
4
|
+
import { makeStyleId } from './utils.js';
|
|
5
|
+
|
|
6
|
+
const SDFText = /*#__PURE__*/forwardRef(function SDFText({
|
|
7
|
+
instanceKey,
|
|
8
|
+
bold,
|
|
9
|
+
color,
|
|
10
|
+
opacity,
|
|
11
|
+
outlineWidth,
|
|
12
|
+
outlineOpacity,
|
|
13
|
+
outlineColor,
|
|
14
|
+
text,
|
|
15
|
+
alignX,
|
|
16
|
+
alignY,
|
|
17
|
+
letterSpacing,
|
|
18
|
+
yShift,
|
|
19
|
+
width,
|
|
20
|
+
lineHeight,
|
|
21
|
+
onBeforeUpdate
|
|
22
|
+
}, ref) {
|
|
23
|
+
const services = useContext(SDFTextProviderContext);
|
|
24
|
+
if (!services) throw new Error('No SDFTextProvider');
|
|
25
|
+
const groupRef = useRef(null);
|
|
26
|
+
const instanceRef = useRef(null);
|
|
27
|
+
useImperativeHandle(ref, () => ({
|
|
28
|
+
setTransform(transform) {
|
|
29
|
+
if (instanceRef.current) {
|
|
30
|
+
instanceRef.current.transform = transform;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}), []);
|
|
34
|
+
useLayoutEffect(() => {
|
|
35
|
+
var _groupRef$current;
|
|
36
|
+
const textStyle = {
|
|
37
|
+
bold,
|
|
38
|
+
color,
|
|
39
|
+
opacity,
|
|
40
|
+
outlineWidth,
|
|
41
|
+
outlineOpacity,
|
|
42
|
+
outlineColor
|
|
43
|
+
};
|
|
44
|
+
const textConfig = {
|
|
45
|
+
text,
|
|
46
|
+
alignX,
|
|
47
|
+
alignY,
|
|
48
|
+
letterSpacing,
|
|
49
|
+
yShift,
|
|
50
|
+
width,
|
|
51
|
+
lineHeight,
|
|
52
|
+
onBeforeUpdate
|
|
53
|
+
};
|
|
54
|
+
const styleId = makeStyleId(textStyle);
|
|
55
|
+
instanceRef.current = services.insertInstance(styleId, textStyle, instanceKey, textConfig, (_groupRef$current = groupRef.current) !== null && _groupRef$current !== void 0 ? _groupRef$current : undefined);
|
|
56
|
+
return () => services.removeInstance(styleId, instanceKey);
|
|
57
|
+
}, [services, instanceKey, text, alignX, alignY, letterSpacing, yShift, width, lineHeight, bold, color, opacity, outlineWidth, outlineOpacity, outlineColor, onBeforeUpdate]);
|
|
58
|
+
return /*#__PURE__*/React.createElement("group", {
|
|
59
|
+
ref: groupRef
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export { SDFText };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),t=require("@react-three/drei"),r=require("@react-three/fiber"),n=require("react"),o=require("three"),a=require("three/examples/jsm/loaders/FontLoader"),l=require("../MSDFTextGeometry/index.cjs.js"),i=require("../register.cjs.js"),u=require("./context.cjs.js"),s=require("./utils.cjs.js");function c(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function d(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}require("quad-indices"),require("../MSDFTextGeometry/TextLayout.cjs.js"),require("word-wrapper"),require("../MSDFTextGeometry/utils.cjs.js"),require("../MSDFTextGeometry/vertices.cjs.js"),require("../MSDFTextMaterial/index.cjs.js");var f=c(e),m=d(n);const y=99999,p=99998,v=new o.Matrix4,x=new o.Matrix4,h=new o.Color("#ffffff"),j=new o.Color("#000000"),g=.01;function M({instances:e,textStyle:t,atlas:o,font:a,atlasBold:i,fontBold:u,...s}){var c;const d=i&&u&&t.bold,M=d?u.data:a.data,S=n.useState((()=>new l({font:M,texts:[]})))[0];n.useLayoutEffect((()=>()=>S.dispose()),[S]),n.useLayoutEffect((()=>{const t=e.map((e=>e.config));S.update({font:M,texts:t})}),[S,e,M]),r.useFrame((()=>{e.forEach(((t,r)=>{null==t.config.onBeforeUpdate||t.config.onBeforeUpdate(),x.identity();const n=e[r].object;n&&(n.updateMatrixWorld(),x.multiply(n.matrixWorld)),x.multiply(t.transform),v.makeScale(g,g,g),x.multiply(v);const o=S._layouts[r];if(o){var a,l,i,u;const e=null!==(a=o.width)&&void 0!==a?a:0,t=null!==(l=o.height)&&void 0!==l?l:0,r=null!==(i=o.lineHeight)&&void 0!==i?i:0,n=null!==(u=o._options.yShift)&&void 0!==u?u:0,s=r/4-t,c=r/2;let d=0;"center"===o._options.alignX&&(d=-e/2),"right"===o._options.alignX&&(d=-e);let f=s+n;"middle"===o._options.alignY&&(f=(s+c)/2-o.ascender+n),"bottom"===o._options.alignY&&(f=c+n),v.makeTranslation(d,f,0),x.multiply(v)}v.makeRotationX(Math.PI),x.multiply(v),S.setTransform(x,r)}))}));const q=n.useMemo((()=>{var e,r;return{uMap:{value:d?i:o},uColor:{value:null!==(e=t.color)&&void 0!==e?e:h},uOpacity:{value:null!==(r=t.opacity)&&void 0!==r?r:1},uOffset:{value:0}}}),[d,t.opacity,t.color,o,i]),F=n.useMemo((()=>{var e,r;return{uMap:{value:d?i:o},uColor:{value:null!==(e=t.outlineColor)&&void 0!==e?e:j},uOpacity:{value:null!==(r=t.outlineOpacity)&&void 0!==r?r:.4},uOffset:{value:t.outlineWidth}}}),[d,t.outlineOpacity,t.outlineColor,t.outlineWidth,o,i]);return m.createElement(m.Fragment,null,m.createElement("mesh",f.default({geometry:S,frustumCulled:!1,renderOrder:y},s),m.createElement("mSDFTextMaterial",{uniforms:q})),(null!==(c=t.outlineWidth)&&void 0!==c?c:0)>0&&m.createElement("mesh",f.default({geometry:S,frustumCulled:!1,renderOrder:p},s),m.createElement("mSDFTextMaterial",{uniforms:F,depthWrite:!1})))}exports.SDFTextProvider=function({children:e,fontPathRegular:l,fontPathBold:c}){n.useMemo(i.registerTextEngineToR3F,[]);const d=t.useTexture(l.sdfPath),f=r.useLoader(a.FontLoader,l.fontPath),y=t.useTexture(c?c.sdfPath:[]),p=r.useLoader(a.FontLoader,c?c.fontPath:[]),v=n.useState((()=>new Map))[0],x=n.useState(0)[1],h=n.useMemo((()=>({insertInstance(e,t,r,n,a){var l;const i={instanceKey:r,config:n,object:a,transform:new o.Matrix4},u=v.get(e);return v.set(e,{textStyle:t,instances:[...null!==(l=null==u?void 0:u.instances)&&void 0!==l?l:[],i]}),x((e=>e+1)),i},removeInstance(e,t){const r=v.get(e);if(r){const n=r.instances.filter((e=>e.instanceKey!==t));0===n.length?v.delete(e):v.set(e,{textStyle:r.textStyle,instances:n}),x((e=>e+1))}}})),[]);return m.createElement(u.SDFTextProviderContext.Provider,{value:h},m.createElement(m.Fragment,null,e,Array.from(v.values()).map((e=>m.createElement(M,{key:s.makeStyleId(e.textStyle),textStyle:e.textStyle,instances:e.instances,atlas:d,font:f,atlasBold:Array.isArray(y)?void 0:y,fontBold:Array.isArray(p)?void 0:p})))))};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
2
|
+
import { useTexture } from '@react-three/drei';
|
|
3
|
+
import { useLoader, useFrame } from '@react-three/fiber';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { useMemo, useState, useLayoutEffect } from 'react';
|
|
6
|
+
import { Matrix4, Color } from 'three';
|
|
7
|
+
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
|
|
8
|
+
import MSDFTextGeometry from '../MSDFTextGeometry/index.js';
|
|
9
|
+
import { registerTextEngineToR3F } from '../register.js';
|
|
10
|
+
import { SDFTextProviderContext } from './context.js';
|
|
11
|
+
import { makeStyleId } from './utils.js';
|
|
12
|
+
|
|
13
|
+
// Since the outline is done with two meshes, we hardcode two render order values
|
|
14
|
+
// to ensure the foreground is rendered after the outline
|
|
15
|
+
const foreRenderOrder = 99999;
|
|
16
|
+
const outlineRenderOrder = 99998;
|
|
17
|
+
const temp = new Matrix4();
|
|
18
|
+
const transformFinal = new Matrix4();
|
|
19
|
+
const white = new Color('#ffffff');
|
|
20
|
+
const black = new Color('#000000');
|
|
21
|
+
const scaleFactor = 0.01;
|
|
22
|
+
function StyleGroupRenderer({
|
|
23
|
+
instances,
|
|
24
|
+
textStyle,
|
|
25
|
+
atlas,
|
|
26
|
+
font,
|
|
27
|
+
atlasBold,
|
|
28
|
+
fontBold,
|
|
29
|
+
...rest
|
|
30
|
+
}) {
|
|
31
|
+
var _textStyle$outlineWid;
|
|
32
|
+
const shouldDoBold = atlasBold && fontBold && textStyle.bold;
|
|
33
|
+
const fontData = shouldDoBold ? fontBold.data : font.data;
|
|
34
|
+
const geometry = useState(() => new MSDFTextGeometry({
|
|
35
|
+
font: fontData,
|
|
36
|
+
texts: []
|
|
37
|
+
}))[0];
|
|
38
|
+
useLayoutEffect(() => () => geometry.dispose(), [geometry]);
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
const texts = instances.map(is => is.config);
|
|
41
|
+
geometry.update({
|
|
42
|
+
font: fontData,
|
|
43
|
+
texts
|
|
44
|
+
});
|
|
45
|
+
}, [geometry, instances, fontData]);
|
|
46
|
+
useFrame(() => {
|
|
47
|
+
// TODO: Revisit this to avoid calculating the matrix every frame
|
|
48
|
+
instances.forEach((inst, index) => {
|
|
49
|
+
inst.config.onBeforeUpdate == null ? void 0 : inst.config.onBeforeUpdate();
|
|
50
|
+
transformFinal.identity();
|
|
51
|
+
const placeholder = instances[index].object;
|
|
52
|
+
if (placeholder) {
|
|
53
|
+
placeholder.updateMatrixWorld();
|
|
54
|
+
transformFinal.multiply(placeholder.matrixWorld);
|
|
55
|
+
}
|
|
56
|
+
transformFinal.multiply(inst.transform);
|
|
57
|
+
temp.makeScale(scaleFactor, scaleFactor, scaleFactor);
|
|
58
|
+
transformFinal.multiply(temp);
|
|
59
|
+
const layout = geometry._layouts[index];
|
|
60
|
+
if (layout) {
|
|
61
|
+
var _layout$width, _layout$height, _layout$lineHeight, _layout$_options$yShi;
|
|
62
|
+
const width = (_layout$width = layout.width) !== null && _layout$width !== void 0 ? _layout$width : 0;
|
|
63
|
+
const height = (_layout$height = layout.height) !== null && _layout$height !== void 0 ? _layout$height : 0;
|
|
64
|
+
const lineHeight = (_layout$lineHeight = layout.lineHeight) !== null && _layout$lineHeight !== void 0 ? _layout$lineHeight : 0;
|
|
65
|
+
const optYShift = (_layout$_options$yShi = layout._options.yShift) !== null && _layout$_options$yShi !== void 0 ? _layout$_options$yShi : 0;
|
|
66
|
+
const bottomShift = -height + lineHeight / 4;
|
|
67
|
+
const topShift = lineHeight / 2;
|
|
68
|
+
let xShift = 0;
|
|
69
|
+
if (layout._options.alignX === 'center') xShift = -width / 2;
|
|
70
|
+
if (layout._options.alignX === 'right') xShift = -width;
|
|
71
|
+
let yShift = bottomShift + optYShift;
|
|
72
|
+
if (layout._options.alignY === 'middle') yShift = (bottomShift + topShift) / 2 - layout.ascender + optYShift;
|
|
73
|
+
if (layout._options.alignY === 'bottom') yShift = topShift + optYShift;
|
|
74
|
+
temp.makeTranslation(xShift, yShift, 0);
|
|
75
|
+
transformFinal.multiply(temp);
|
|
76
|
+
}
|
|
77
|
+
temp.makeRotationX(Math.PI);
|
|
78
|
+
transformFinal.multiply(temp);
|
|
79
|
+
geometry.setTransform(transformFinal, index);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
const uniforms = useMemo(() => {
|
|
83
|
+
var _textStyle$color, _textStyle$opacity;
|
|
84
|
+
return {
|
|
85
|
+
uMap: {
|
|
86
|
+
value: shouldDoBold ? atlasBold : atlas
|
|
87
|
+
},
|
|
88
|
+
uColor: {
|
|
89
|
+
value: (_textStyle$color = textStyle.color) !== null && _textStyle$color !== void 0 ? _textStyle$color : white
|
|
90
|
+
},
|
|
91
|
+
uOpacity: {
|
|
92
|
+
value: (_textStyle$opacity = textStyle.opacity) !== null && _textStyle$opacity !== void 0 ? _textStyle$opacity : 1.0
|
|
93
|
+
},
|
|
94
|
+
uOffset: {
|
|
95
|
+
value: 0
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}, [shouldDoBold, textStyle.opacity, textStyle.color, atlas, atlasBold]);
|
|
99
|
+
const uniformsOutline = useMemo(() => {
|
|
100
|
+
var _textStyle$outlineCol, _textStyle$outlineOpa;
|
|
101
|
+
return {
|
|
102
|
+
uMap: {
|
|
103
|
+
value: shouldDoBold ? atlasBold : atlas
|
|
104
|
+
},
|
|
105
|
+
uColor: {
|
|
106
|
+
value: (_textStyle$outlineCol = textStyle.outlineColor) !== null && _textStyle$outlineCol !== void 0 ? _textStyle$outlineCol : black
|
|
107
|
+
},
|
|
108
|
+
uOpacity: {
|
|
109
|
+
value: (_textStyle$outlineOpa = textStyle.outlineOpacity) !== null && _textStyle$outlineOpa !== void 0 ? _textStyle$outlineOpa : 0.4
|
|
110
|
+
},
|
|
111
|
+
uOffset: {
|
|
112
|
+
value: textStyle.outlineWidth
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}, [shouldDoBold, textStyle.outlineOpacity, textStyle.outlineColor, textStyle.outlineWidth, atlas, atlasBold]);
|
|
116
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("mesh", _extends({
|
|
117
|
+
geometry: geometry,
|
|
118
|
+
frustumCulled: false,
|
|
119
|
+
renderOrder: foreRenderOrder
|
|
120
|
+
}, rest), /*#__PURE__*/React.createElement("mSDFTextMaterial", {
|
|
121
|
+
uniforms: uniforms
|
|
122
|
+
})), ((_textStyle$outlineWid = textStyle.outlineWidth) !== null && _textStyle$outlineWid !== void 0 ? _textStyle$outlineWid : 0) > 0 && /*#__PURE__*/React.createElement("mesh", _extends({
|
|
123
|
+
geometry: geometry,
|
|
124
|
+
frustumCulled: false,
|
|
125
|
+
renderOrder: outlineRenderOrder
|
|
126
|
+
}, rest), /*#__PURE__*/React.createElement("mSDFTextMaterial", {
|
|
127
|
+
uniforms: uniformsOutline,
|
|
128
|
+
depthWrite: false
|
|
129
|
+
})));
|
|
130
|
+
}
|
|
131
|
+
function SDFTextProvider({
|
|
132
|
+
children,
|
|
133
|
+
fontPathRegular,
|
|
134
|
+
fontPathBold
|
|
135
|
+
}) {
|
|
136
|
+
useMemo(registerTextEngineToR3F, []);
|
|
137
|
+
const atlas = useTexture(fontPathRegular.sdfPath);
|
|
138
|
+
const font = useLoader(FontLoader, fontPathRegular.fontPath);
|
|
139
|
+
const atlasBold = useTexture(fontPathBold ? fontPathBold.sdfPath : []);
|
|
140
|
+
const fontBold = useLoader(FontLoader, fontPathBold ? fontPathBold.fontPath : []);
|
|
141
|
+
|
|
142
|
+
// We have a mutable map of immutable arrays, so we are using a force update
|
|
143
|
+
const instancesGroups = useState(() => new Map())[0];
|
|
144
|
+
const forceUpdate = useState(0)[1];
|
|
145
|
+
const api = useMemo(() => ({
|
|
146
|
+
insertInstance(styleId, textStyle, instanceKey, config, object) {
|
|
147
|
+
var _prevRecord$instances;
|
|
148
|
+
const newInst = {
|
|
149
|
+
instanceKey,
|
|
150
|
+
config,
|
|
151
|
+
object,
|
|
152
|
+
transform: new Matrix4()
|
|
153
|
+
};
|
|
154
|
+
const prevRecord = instancesGroups.get(styleId);
|
|
155
|
+
instancesGroups.set(styleId, {
|
|
156
|
+
textStyle,
|
|
157
|
+
instances: [...((_prevRecord$instances = prevRecord == null ? void 0 : prevRecord.instances) !== null && _prevRecord$instances !== void 0 ? _prevRecord$instances : []), newInst]
|
|
158
|
+
});
|
|
159
|
+
forceUpdate(i => i + 1);
|
|
160
|
+
return newInst;
|
|
161
|
+
},
|
|
162
|
+
removeInstance(styleId, instanceKey) {
|
|
163
|
+
const prevRecord = instancesGroups.get(styleId);
|
|
164
|
+
if (prevRecord) {
|
|
165
|
+
const without = prevRecord.instances.filter(inst => inst.instanceKey !== instanceKey);
|
|
166
|
+
if (without.length === 0) {
|
|
167
|
+
instancesGroups.delete(styleId);
|
|
168
|
+
} else {
|
|
169
|
+
instancesGroups.set(styleId, {
|
|
170
|
+
textStyle: prevRecord.textStyle,
|
|
171
|
+
instances: without
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
forceUpdate(i => i + 1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}), []);
|
|
178
|
+
return /*#__PURE__*/React.createElement(SDFTextProviderContext.Provider, {
|
|
179
|
+
value: api
|
|
180
|
+
}, /*#__PURE__*/React.createElement(React.Fragment, null, children, Array.from(instancesGroups.values()).map(ig => /*#__PURE__*/React.createElement(StyleGroupRenderer, {
|
|
181
|
+
key: makeStyleId(ig.textStyle),
|
|
182
|
+
textStyle: ig.textStyle,
|
|
183
|
+
instances: ig.instances,
|
|
184
|
+
atlas: atlas,
|
|
185
|
+
font: font,
|
|
186
|
+
atlasBold: Array.isArray(atlasBold) ? undefined : atlasBold,
|
|
187
|
+
fontBold: Array.isArray(fontBold) ? undefined : fontBold
|
|
188
|
+
}))));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { SDFTextProvider };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=require("react").createContext(null);exports.SDFTextProviderContext=e;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Color, Matrix4, Object3D } from 'three';
|
|
2
|
+
export interface ISDFTextStyle {
|
|
3
|
+
bold?: boolean;
|
|
4
|
+
color?: Color;
|
|
5
|
+
opacity?: number;
|
|
6
|
+
outlineWidth?: number;
|
|
7
|
+
outlineOpacity?: number;
|
|
8
|
+
outlineColor?: Color;
|
|
9
|
+
}
|
|
10
|
+
export interface ISDFTextInstance {
|
|
11
|
+
config: ISDFTextConfig;
|
|
12
|
+
instanceKey: string;
|
|
13
|
+
object?: Object3D;
|
|
14
|
+
transform: Matrix4;
|
|
15
|
+
}
|
|
16
|
+
export interface ISDFTextConfig {
|
|
17
|
+
text: string;
|
|
18
|
+
alignX?: 'left' | 'center' | 'right';
|
|
19
|
+
alignY?: 'top' | 'middle' | 'bottom';
|
|
20
|
+
letterSpacing?: number;
|
|
21
|
+
width?: number;
|
|
22
|
+
lineHeight?: number;
|
|
23
|
+
yShift?: number;
|
|
24
|
+
onBeforeUpdate?: () => void;
|
|
25
|
+
}
|
|
26
|
+
export interface ISDFTextServices {
|
|
27
|
+
insertInstance(styleId: string, textStyle: ISDFTextStyle, instanceKey: string, config: ISDFTextConfig, object?: Object3D): ISDFTextInstance;
|
|
28
|
+
removeInstance(styleId: string, instanceKey: string): void;
|
|
29
|
+
}
|
|
30
|
+
export interface ISDFFont {
|
|
31
|
+
sdfPath: string;
|
|
32
|
+
fontPath: string;
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});exports.makeStyleId=e=>{var t,o;return[e.bold,null==(t=e.color)?void 0:t.getHexString(),e.opacity,e.outlineWidth,e.outlineOpacity,null==(o=e.outlineColor)?void 0:o.getHexString()].join("_")};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const makeStyleId = style => {
|
|
2
|
+
var _style$color, _style$outlineColor;
|
|
3
|
+
return [style.bold, (_style$color = style.color) == null ? void 0 : _style$color.getHexString(), style.opacity, style.outlineWidth, style.outlineOpacity, (_style$outlineColor = style.outlineColor) == null ? void 0 : _style$outlineColor.getHexString()].join('_');
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export { makeStyleId };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@react-three/fiber"),r=require("./MSDFTextGeometry/index.cjs.js"),t=require("./MSDFTextMaterial/index.cjs.js");require("quad-indices"),require("three"),require("./MSDFTextGeometry/TextLayout.cjs.js"),require("word-wrapper"),require("./MSDFTextGeometry/utils.cjs.js"),require("./MSDFTextGeometry/vertices.cjs.js"),exports.registerTextEngineToR3F=function(){e.extend({MSDFTextMaterial:t,MSDFTextGeometry:r})};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactThreeFiber } from '@react-three/fiber';
|
|
2
|
+
import MSDFTextGeometry from './MSDFTextGeometry';
|
|
3
|
+
import MSDFTextMaterial from './MSDFTextMaterial';
|
|
4
|
+
export declare function registerTextEngineToR3F(): void;
|
|
5
|
+
declare global {
|
|
6
|
+
namespace JSX {
|
|
7
|
+
interface IntrinsicElements {
|
|
8
|
+
mSDFTextMaterial: ReactThreeFiber.MaterialNode<MSDFTextMaterial, typeof MSDFTextMaterial>;
|
|
9
|
+
mSDFTextGeometry: ReactThreeFiber.BufferGeometryNode<MSDFTextGeometry, typeof MSDFTextGeometry>;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { extend } from '@react-three/fiber';
|
|
2
|
+
import MSDFTextGeometry from './MSDFTextGeometry/index.js';
|
|
3
|
+
import MSDFTextMaterial from './MSDFTextMaterial/index.js';
|
|
4
|
+
|
|
5
|
+
function registerTextEngineToR3F() {
|
|
6
|
+
extend({
|
|
7
|
+
MSDFTextMaterial,
|
|
8
|
+
MSDFTextGeometry
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { registerTextEngineToR3F };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),t=require("react"),n=require("three"),r=require("./InstancedTextProvider.cjs.js"),o=require("./types.cjs.js");function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function u(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}require("@react-three/fiber"),require("./instancedTextBuffers.cjs.js"),require("./InstancedTextPlaceholder.cjs.js"),require("./useInstancedTextMaterial.cjs.js"),require("@react-three/drei");var c=a(e),i=u(t);const s=new n.Color("white");exports.InstancedText=function({text:e,thickness:a,fontSize:u,textAlign:d,scale:l,color:f,opacity:g,font:x,...y}){var b,M,h;const m=x.fontMetadata.charToIndex["?"],p=r.useInstancedTextAPI(),B=t.useRef(null),v=t.useRef(null);t.useLayoutEffect((()=>{const t=B.current;if(!t)return;const r=x.fontMetadata.fontScale,u=(x.fontMetadata.boundingBox.yMax-x.fontMetadata.boundingBox.yMin+x.fontMetadata.underlineThickness)*r,c=new Float32Array(e.length),i=new Float32Array(2*e.length);let l=0,g=0;const y=[];Array.from(e).forEach(((e,t)=>{if("\n"===e)y.push(l),l=0,g-=u;else{var n;const o=x.fontMetadata.charToIndex[e],a=null!==(n=x.fontMetadata.advances[o])&&void 0!==n?n:x.fontMetadata.advances[m];c[t]=r,i[2*t]=l,i[2*t+1]=g,l+=a*r}})),y.push(l);const b=Math.max(...y),M=-((x.fontMetadata.boundingBox.yMax-x.fontMetadata.boundingBox.yMin)/2+x.fontMetadata.boundingBox.yMin)*r;Array.from(e).forEach(((e,t)=>{c[t]>0&&(d===o.TextAlignment.left&&(i[2*t+1]+=M),d===o.TextAlignment.right&&(i[2*t]-=b,i[2*t+1]+=M),d===o.TextAlignment.center&&(i[2*t]-=b/2,i[2*t+1]+=M))})),t.geometry.boundingBox||(t.geometry.boundingBox=new n.Box3),t.geometry.boundingSphere||(t.geometry.boundingSphere=new n.Sphere),t.geometry.boundingBox.setFromPoints([new n.Vector3(0,x.fontMetadata.boundingBox.yMin*r,0),new n.Vector3(b,x.fontMetadata.boundingBox.yMax*r,a)]),t.geometry.boundingSphere.setFromPoints([new n.Vector3(0,x.fontMetadata.boundingBox.yMin*r,0),new n.Vector3(b,x.fontMetadata.boundingBox.yMax*r,a)]),d===o.TextAlignment.left&&(t.geometry.boundingBox.translate(new n.Vector3(0,M,0)),t.geometry.boundingSphere.translate(new n.Vector3(0,M,0))),d===o.TextAlignment.right&&(t.geometry.boundingBox.translate(new n.Vector3(-b,M,0)),t.geometry.boundingSphere.translate(new n.Vector3(-b,M,0))),d===o.TextAlignment.center&&(t.geometry.boundingBox.translate(new n.Vector3(-b/2,M,0)),t.geometry.boundingSphere.translate(new n.Vector3(-b/2,M,0)));const h=p.addInstances(e,x,null!=f?f:s,i,t);return h?(v.current=h,()=>p.removeInstances(h)):void 0}),[e,f,x.fontMetadata,m,a,d,p]),t.useEffect((()=>{v.current&&p.setOpacity(v.current,null!=g?g:1)}),[p,g]);const T=t.useMemo((()=>({getBuffers:()=>v.current&&p.getBuffers(v.current),getPtr:()=>v.current,setGlyphs:e=>{if(!v.current)return;const t=p.getBuffers(v.current);if(!t)return;const n=Array.from(e).map((e=>x.fontMetadata.charToIndex[e]));t.instanceGIndexBuffer.set(n,v.current.start),t.instanceGIndexBufferAttribute.needsUpdate=!0}})),[x.fontMetadata.charToIndex]);return i.createElement("instancedTextPlaceholder",c.default({scale:[u*(null!==(b=null==l?void 0:l.x)&&void 0!==b?b:1),u*(null!==(M=null==l?void 0:l.y)&&void 0!==M?M:1),a*(null!==(h=null==l?void 0:l.z)&&void 0!==h?h:1)]},y,{textAPI:T,ref:B,frustumCulled:!1}))};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GroupProps } from '@react-three/fiber';
|
|
2
|
+
import { Color, Vector3 } from 'three';
|
|
3
|
+
import { Font3D, TextAlignment } from './types';
|
|
4
|
+
export declare function InstancedText({ text, thickness, fontSize, textAlign, scale, color, opacity, font, ...rest }: {
|
|
5
|
+
text: string;
|
|
6
|
+
thickness: number;
|
|
7
|
+
fontSize: number;
|
|
8
|
+
textAlign?: TextAlignment;
|
|
9
|
+
scale?: Vector3;
|
|
10
|
+
color?: Color;
|
|
11
|
+
opacity?: number;
|
|
12
|
+
font: Font3D;
|
|
13
|
+
} & GroupProps): JSX.Element;
|