Setting the file. One moment.
Text Refine · Frost Sequence Camera Orbit · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Three Mesh BVH LICENSE
Number 10.68
Position 68 of 76
Type TypeScript
Size 6 KB
Lines 188 source/src/shape/ textRefine.ts
TypeScript · 188 lines · 6 KB
Math.
max
(
1
, Math.
min
(
4
, Math.
round
(value)))
8 : TEXT_MESH_DETAIL_DEFAULT ;
9 }
10 export function textRefinementSettings ( scale : number , detail : number ) {
11 const level = resolveTextMeshDetail (detail);
12 return { maxEdge: scale / ( 2 * level), extraTriangles: 160_000 * level };
13 }
14 type Edge = { a : number ; b : number ; length2 : number ; faces : Set < number > };
15 /** Exported for measurement before displacement; positions gain shared midpoint vertices. */
16 export function refineText (
17 positions : number [],
18 initial : number [],
19 maxEdge : number ,
20 extraTriangles : number ,
21 ) {
22 const triangles : ( number [] | null )[] = [],
23 edges = new Map < string , Edge >(),
24 heap : Edge [] = [];
25 const max2 = maxEdge * maxEdge,
26 limit = initial. length / 3 + extraTriangles;
27 let count = initial. length / 3 ;
28 const key = ( a : number , b : number ) => (a < b ? `${ a }:${ b }` : `${ b }:${ a }` );
29 const better = ( a : Edge , b : Edge ) =>
30 a.length2 > b.length2 || (a.length2 === b.length2 && (a.a < b.a || (a.a === b.a && a.b < b.b)));
31 function push ( edge : Edge ) {
32 let i = heap. length ;
33 heap. push (edge);
34 while (i > 0 ) {
35 const parent = (i - 1 ) >> 1 ;
36 if ( ! better (edge, heap[parent])) break ;
37 heap[i] = heap[parent];
38 i = parent;
39 }
40 heap[i] = edge;
41 }
42 function pop () {
43 const best = heap[ 0 ],
44 last = heap. pop () ! ;
45 if (heap. length ) {
46 let i = 0 ;
47 while (i * 2 + 1 < heap. length ) {
48 let child = i * 2 + 1 ;
49 if (child + 1 < heap. length && better (heap[child + 1 ], heap[child])) child ++ ;
50 if ( ! better (heap[child], last)) break ;
51 heap[i] = heap[child];
52 i = child;
53 }
54 heap[i] = last;
55 }
56 return best;
57 }
58 function add ( a : number , b : number , c : number ) {
59 const id = triangles. length ;
60 triangles. push ([a, b, c]);
61 for ( const [ u , v ] of [
62 [a, b],
63 [b, c],
64 [c, a],
65 ]) {
66 const k = key (u, v);
67 let edge = edges. get (k);
68 if ( ! edge) {
69 const dx = positions[u * 3 ] - positions[v * 3 ],
70 dy = positions[u * 3 + 1 ] - positions[v * 3 + 1 ],
71 dz = positions[u * 3 + 2 ] - positions[v * 3 + 2 ];
72 edge = {
73 a: Math. min (u, v),
74 b: Math. max (u, v),
75 length2: dx * dx + dy * dy + dz * dz,
76 faces: new Set (),
77 };
78 edges. set (k, edge);
79 if (edge.length2 > max2) push (edge);
80 }
81 edge.faces. add (id);
82 }
83 }
84 function remove ( id : number ) {
85 const t = triangles[id] ! ;
86 triangles[id] = null ;
87 for ( let j = 0 ; j < 3 ; j ++ ) {
88 const k = key (t[j], t[(j + 1 ) % 3 ]),
89 e = edges. get (k) ! ;
90 e.faces. delete (id);
91 if ( ! e.faces.size) edges. delete (k);
92 }
93 }
94 for ( let i = 0 ; i < initial. length ; i += 3 ) add (initial[i], initial[i + 1 ], initial[i + 2 ]);
95 while (heap. length && count < limit) {
96 const edge = pop ();
97 if (edges. get ( key (edge.a, edge.b)) !== edge) continue ;
98 const ids = [ ... edge.faces];
99 if (count + ids. length > limit) continue ;
100 const m = positions. length / 3 ;
101 positions. push (
102 (positions[edge.a * 3 ] + positions[edge.b * 3 ]) / 2 ,
103 (positions[edge.a * 3 + 1 ] + positions[edge.b * 3 + 1 ]) / 2 ,
104 (positions[edge.a * 3 + 2 ] + positions[edge.b * 3 + 2 ]) / 2 ,
105 );
106 const faces = ids. map (( id ) => triangles[id] ! );
107 for ( const id of ids) remove (id);
108 for ( const t of faces) {
109 // Retain original winding, regardless of which side sees a->b versus b->a.
110 const j = t. findIndex (( v , j ) => key (v, t[(j + 1 ) % 3 ]) === key (edge.a, edge.b));
111 const a = t[j],
112 b = t[(j + 1 ) % 3 ],
113 c = t[(j + 2 ) % 3 ];
114 add (a, m, c);
115 add (m, b, c);
116 }
117 count += ids. length ;
118 }
119 const out : number [] = [];
120 for ( const t of triangles) if (t) out. push (t[ 0 ], t[ 1 ], t[ 2 ]);
121 return out;
122 }
123
124 /** Repair subpixel extrusion seam duplicates before subdividing them. Only boundary
125 * vertices are eligible; real glyph contours/counters and the SVG path are untouched. */
126 export function repairTextSeams ( positions : number [], indices : number []) {
127 const clean : number [] = [];
128 for ( let i = 0 ; i < indices. length ; i += 3 ) {
129 const [ a , b , c ] = indices. slice (i, i + 3 );
130 if (a !== b && b !== c && c !== a) clean. push (a, b, c);
131 }
132 const edges = new Map < string , { a : number ; b : number ; count : number }>();
133 for ( let i = 0 ; i < clean. length ; i += 3 )
134 for ( const [ a , b ] of [
135 [clean[i], clean[i + 1 ]],
136 [clean[i + 1 ], clean[i + 2 ]],
137 [clean[i + 2 ], clean[i]],
138 ]) {
139 const key = a < b ? `${ a }:${ b }` : `${ b }:${ a }` ,
140 edge = edges. get (key);
141 if (edge) edge.count ++ ;
142 else edges. set (key, { a, b, count: 1 });
143 }
144 const boundary = new Set < number >();
145 for ( const e of edges. values ())
146 if (e.count === 1 ) {
147 boundary. add (e.a);
148 boundary. add (e.b);
149 }
150 const eps = 1e-5 ,
151 grid = new Map < string , number []>(),
152 remap = new Map < number , number >();
153 for ( const v of [ ... boundary]. sort (( a , b ) => a - b)) {
154 const x = positions[v * 3 ],
155 y = positions[v * 3 + 1 ],
156 z = positions[v * 3 + 2 ],
157 cx = Math. floor (x / eps),
158 cy = Math. floor (y / eps),
159 cz = Math. floor (z / eps);
160 let representative = v;
161 for ( let dx = - 1 ; dx <= 1 ; dx ++ )
162 for ( let dy = - 1 ; dy <= 1 ; dy ++ )
163 for ( let dz = - 1 ; dz <= 1 ; dz ++ )
164 for ( const other of grid. get ( `${ cx + dx }:${ cy + dy }:${ cz + dz }` ) || []) {
165 if (
166 Math. hypot (
167 x - positions[other * 3 ],
168 y - positions[other * 3 + 1 ],
169 z - positions[other * 3 + 2 ],
170 ) <= eps
171 )
172 representative = Math. min (representative, other);
173 }
174 remap. set (v, representative);
175 if (representative === v) {
176 const key = `${ cx }:${ cy }:${ cz }` ,
177 bucket = grid. get (key) || [];
178 bucket. push (v);
179 grid. set (key, bucket);
180 }
181 }
182 const out : number [] = [];
183 for ( let i = 0 ; i < clean. length ; i += 3 ) {
184 const [ a , b , c ] = clean. slice (i, i + 3 ). map (( v ) => remap. get (v) ?? v);
185 if (a !== b && b !== c && c !== a) out. push (a, b, c);
186 }
187 return out;
188 }