11
22namespace Polymesh {
33
4+ export function finv ( x : number ) : number {
5+ if ( x === 0 ) return Infinity ;
6+ if ( x < 0 ) return - finv ( - x ) ;
7+
8+ // 1. Range Scaling
9+ // ปรับ x ให้อยู่ในช่วง [0.5, 1.0] เพื่อความแม่นยำของพหุนาม
10+ // โดยการคูณด้วย 0.5 หรือ 2 (เทียบเท่าการเลื่อน Bit)
11+ let scale = 1 ;
12+ while ( x < 0.5 ) x *= 2 , scale *= 2 ;
13+ while ( x > 1.0 ) x *= 0.5 , scale *= 0.5 ;
14+
15+ /**
16+ * 2. Polynomial Approximation (Degree 2)
17+ * สูตร: f(x) ≈ a*x^2 + b*x + c
18+ * สำหรับช่วง [0.5, 1.0] สัมประสิทธิ์ที่เหมาะสมคือ:
19+ */
20+ const a = 1.4545 ;
21+ const b = - 4.3636 ;
22+ const c = 3.9091 ;
23+
24+ // คำนวณด้วย Horner's Method: (a*x + b)*x + c
25+ let y = ( a * x + b ) * x + c ;
26+
27+ // 3. Scaling กลับ
28+ return y * scale ;
29+ }
30+
431 const PI = Math . PI ;
532 const TWO_PI = PI * 2 ;
633
@@ -42,8 +69,8 @@ namespace Polymesh {
4269
4370 // ใช้ Loop ปรับค่า scale (ใช้การคูณ/บรรทัดคำนวณสั้นๆ แทนการหาร)
4471 // การคูณด้วย 0.25 หรือ 4 เร็วมากในระดับ CPU
45- while ( x < 0.5 ) { x *= 4 ; scale *= 0.5 ; }
46- while ( x > 2.0 ) { x *= 0.25 ; scale *= 2 ; }
72+ while ( x < 0.5 ) x *= 4 , scale *= 0.5 ;
73+ while ( x > 2.0 ) x *= 0.25 , scale *= 2 ;
4774
4875 /**
4976 * 2. High-Degree Polynomial Approximation
@@ -278,28 +305,31 @@ namespace Polymesh {
278305
279306 export function isOutOfArea ( x : number , y : number , width : number , height : number , scale ?: number ) { return ( isOutOfRange ( x , width , scale ) || isOutOfRange ( y , height , scale ) ) ; }
280307
281- export function avgZ ( rot : Vector3 [ ] , inds : number [ ] ) { return ( inds . reduce ( ( s , i ) => s + rot [ i ] . z , 0 ) / inds . length ) ; }
308+ export function avgZ ( rot : Vector3 [ ] , inds : number [ ] ) { const invIndsLen = finv ( inds . length ) ; return ( inds . reduce ( ( s , i ) => s + rot [ i ] . z , 0 ) * invIndsLen ) ; }
282309
283- export function avgXYZ ( rot : Vector3 [ ] , inds : number [ ] ) { return new Vector3 (
284- ( inds . reduce ( ( s , i ) => s + rot [ i ] . x , 0 ) / inds . length ) ,
285- ( inds . reduce ( ( s , i ) => s + rot [ i ] . y , 0 ) / inds . length ) ,
286- ( inds . reduce ( ( s , i ) => s + rot [ i ] . z , 0 ) / inds . length ) ,
310+ export function avgXYZ ( rot : Vector3 [ ] , inds : number [ ] ) { const invIndsLen = finv ( inds . length ) ;
311+ return new Vector3 (
312+ ( inds . reduce ( ( s , i ) => s + rot [ i ] . x , 0 ) * invIndsLen ) ,
313+ ( inds . reduce ( ( s , i ) => s + rot [ i ] . y , 0 ) * invIndsLen ) ,
314+ ( inds . reduce ( ( s , i ) => s + rot [ i ] . z , 0 ) * invIndsLen ) ,
287315 ) ;
288316 }
289317
290318 export function farZ ( rot : Vector3 [ ] , inds : number [ ] ) { return ( inds . reduce ( ( s , i ) => Math . max ( s , rot [ i ] . z ) , rot [ 0 ] . z ) ) * inds . length ; }
291319
292- export function avgZs ( rot : Vector3 [ ] [ ] , n : number , inds : number [ ] ) { return ( inds . reduce ( ( s , i ) => s + rot [ i ] [ n ] . z , 0 ) / inds . length ) ; }
320+ export function avgZs ( rot : Vector3 [ ] [ ] , n : number , inds : number [ ] ) { const invIndsLen = finv ( inds . length ) ; return ( inds . reduce ( ( s , i ) => s + rot [ i ] [ n ] . z , 0 ) * invIndsLen ) ; }
293321
294322 export function isEmptyImage ( img : Image ) { return img . equals ( image . create ( img . width , img . height ) ) ; }
295323
296324 export function isOutOfAreaOnFace ( rotated : { x : number , y : number } [ ] , ind : number [ ] , width : number , height : number ) {
297- const avgXYs = new Pt ( ind . reduce ( ( cur , i ) => cur + rotated [ i ] . x , 0 ) / ind . length , ind . reduce ( ( cur , i ) => cur + rotated [ i ] . y , 0 ) / ind . length ) ;
325+ const invIndsLen = finv ( ind . length )
326+ const avgXYs = new Pt ( ind . reduce ( ( cur , i ) => cur + rotated [ i ] . x , 0 ) * invIndsLen , ind . reduce ( ( cur , i ) => cur + rotated [ i ] . y , 0 ) * invIndsLen ) ;
298327 return isOutOfArea ( avgXYs . x , avgXYs . y , width , height , 5 )
299328 }
300329
301330 export function isOutOfAreaOnAvg ( point2s : { x : number , y : number } [ ] , width : number , height : number ) {
302- const avgXYs = new Pt ( point2s . reduce ( ( cur , val ) => cur + val . x , 0 ) / point2s . length , point2s . reduce ( ( cur , val ) => cur + val . y , 0 ) / point2s . length ) ;
331+ const invPt2sLen = finv ( point2s . length ) ;
332+ const avgXYs = new Pt ( point2s . reduce ( ( cur , val ) => cur + val . x , 0 ) * invPt2sLen , point2s . reduce ( ( cur , val ) => cur + val . y , 0 ) * invPt2sLen ) ;
303333 return isOutOfArea ( avgXYs . x , avgXYs . y , width , height , 5 )
304334 }
305335
@@ -371,14 +401,14 @@ namespace Polymesh {
371401 if ( Polymesh . isEmptyImage ( from ) ) return ;
372402 if ( ! p3 ) p3 = new Pt ( p2 . x + ( p1 . x - p0 . x ) , p2 . y + ( p1 . y - p0 . y ) ) ;
373403 const w = from . width , h = from . height ;
374- const w_ = ( 1 / w ) , h_ = ( 1 / h ) ;
404+ const wInv = finv ( w * 1.082 ) , hInv = finv ( h ) ;
375405 const fromRowBuf = pins . createBuffer ( h ) ;
376406 const emptyHash = fromRowBuf . hash ( 0xffff )
377407 for ( let sx = 0 ; sx < w ; sx ++ ) {
378408 const ix = zigzet ( 0 , w - 1 , sx , center )
379409 from . getRows ( w - ix - 1 , fromRowBuf )
380410 if ( fromRowBuf . hash ( 0xffff ) === emptyHash ) continue ;
381- const u0 = ( ix * w_ ) , u1 = ( ( ix + 1 ) * w_ ) ;
411+ const u0 = ( ix * wInv ) , u1 = ( ( ix + 1 ) * wInv ) ;
382412 const qu = [ u0 , u1 ] . map ( u => ( new Ptl (
383413 p0 . x + ( p1 . x - p0 . x ) * u ,
384414 p0 . y + ( p1 . y - p0 . y ) * u ,
@@ -390,7 +420,7 @@ namespace Polymesh {
390420 if ( fromRowBuf . hash ( 0xffff ) === emptyHash ) continue ;
391421 const color = fromRowBuf [ 0 ]
392422 if ( color < 1 ) continue ; // transparent
393- const v0 = ( iy * h_ ) , v1 = ( ( iy + 1 ) * h_ ) ;
423+ const v0 = ( iy * hInv ) , v1 = ( ( iy + 1 ) * hInv ) ;
394424 // Map quad on 1 pixel
395425 const qv = [ v0 , v0 , v1 , v1 ] . map ( ( v , i ) => { const i_2 = i & 1 ;
396426 return new Pt (
0 commit comments