Skip to content

Commit 7b89d1e

Browse files
committed
Update help.ts, render.ts
1 parent ff0234e commit 7b89d1e

File tree

2 files changed

+52
-22
lines changed

2 files changed

+52
-22
lines changed

help.ts

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11

22
namespace 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(

render.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ namespace Polymesh {
6060
y += (y === 0 ? 0 : Math.sign(y) * vsum);
6161
z += (z === 0 ? 0 : Math.sign(z) * vsum);
6262
// Perspective
63-
const scale = Math.abs(dist) / (Math.abs(dist) + z);
63+
const scale = Math.abs(dist) * finv(Math.abs(dist) + z);
6464
return new Vector3(
6565
centerX + x * scale * zoom,
6666
centerY + y * scale * zoom,
@@ -87,13 +87,13 @@ namespace Polymesh {
8787
const inds_ = [];
8888
if (inds.length > 2) inds_[0] = [t.indices[0], t.indices[1], t.indices[2]];
8989
if (inds.length > 3) inds_[1] = [t.indices[3], t.indices[1], t.indices[2]];
90-
const scale = (Math.abs(dist) / (Math.abs(dist) + avgZ(rotated, t.indices)));
90+
const scale = (Math.abs(dist) * finv(Math.abs(dist) + avgZ(rotated, t.indices)));
9191
// LOD calculating?
9292
if (t.img) {
9393
im = t.img.clone();
9494
if (msh.flag.texStream) {
9595
const scaleD = (scale * zoom)
96-
im = t.imgs[Math.clamp(0, t.imgs.length - 1, Math.trunc((psqrt(scaleD / 1.5) * PHI) * (t.imgs.length - 1)))]
96+
im = t.imgs[Math.clamp(0, t.imgs.length - 1, Math.trunc((psqrt(scaleD * 0.75) * PHI) * (t.imgs.length - 1)))]
9797
if (im == null) im = image.create(1, 1)
9898
}
9999
}
@@ -117,8 +117,8 @@ namespace Polymesh {
117117
// set scale image from camera distance
118118
const baseW = im.width;
119119
const baseH = im.height;
120-
const halfW = (baseW / 3) * scale * t.scale * zoom;
121-
const halfH = (baseH / 3) * scale * t.scale * zoom;
120+
const halfW = (baseW * 0.33333333) * scale * t.scale * zoom;
121+
const halfH = (baseH * 0.33333333) * scale * t.scale * zoom;
122122

123123
bq[0].x += halfW, bq[0].y += halfH
124124
bq[1].x -= halfW, bq[1].y += halfH
@@ -159,8 +159,8 @@ namespace Polymesh {
159159
const baseW = im.width;
160160
const baseH = im.height;
161161

162-
halfW = (baseW / 3) * scale * t.scale * zoom;
163-
halfH = (baseH / 3) * scale * t.scale * zoom;
162+
halfW = (baseW * 0.33333333) * scale * t.scale * zoom;
163+
halfH = (baseH * 0.33333333) * scale * t.scale * zoom;
164164

165165
square = Polymesh.gcd(halfW, halfH)
166166
};
@@ -174,8 +174,8 @@ namespace Polymesh {
174174
// fill circle if image is empty
175175
if (isEmptyImage(t.img)) { fillCircleImage(output, cx, cy, square, t.color); continue; }
176176

177-
halfW /= 1.5;
178-
halfH /= 1.5;
177+
halfW *= 0.75;
178+
halfH *= 0.75;
179179

180180
// Draw Simple 2D image (billboard) as quad pixel on image
181181
// use distortImage or drawing without perspective distortion

0 commit comments

Comments
 (0)