@triforge/uv-core 0.1.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/dist/core/BaseUnwrapper.d.ts +24 -0
- package/dist/core/BaseUnwrapper.d.ts.map +1 -0
- package/dist/core/BaseUnwrapper.js +16 -0
- package/dist/core/BaseUnwrapper.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/AverageIslandScale.d.ts +15 -0
- package/dist/operations/AverageIslandScale.d.ts.map +1 -0
- package/dist/operations/AverageIslandScale.js +109 -0
- package/dist/operations/AverageIslandScale.js.map +1 -0
- package/dist/operations/MarkSeams.d.ts +34 -0
- package/dist/operations/MarkSeams.d.ts.map +1 -0
- package/dist/operations/MarkSeams.js +41 -0
- package/dist/operations/MarkSeams.js.map +1 -0
- package/dist/operations/PackIslands.d.ts +22 -0
- package/dist/operations/PackIslands.d.ts.map +1 -0
- package/dist/operations/PackIslands.js +129 -0
- package/dist/operations/PackIslands.js.map +1 -0
- package/dist/unwrappers/AngleBasedABF.d.ts +30 -0
- package/dist/unwrappers/AngleBasedABF.d.ts.map +1 -0
- package/dist/unwrappers/AngleBasedABF.js +162 -0
- package/dist/unwrappers/AngleBasedABF.js.map +1 -0
- package/dist/unwrappers/ConformalLSCM.d.ts +29 -0
- package/dist/unwrappers/ConformalLSCM.d.ts.map +1 -0
- package/dist/unwrappers/ConformalLSCM.js +165 -0
- package/dist/unwrappers/ConformalLSCM.js.map +1 -0
- package/dist/unwrappers/CubeProjection.d.ts +22 -0
- package/dist/unwrappers/CubeProjection.d.ts.map +1 -0
- package/dist/unwrappers/CubeProjection.js +57 -0
- package/dist/unwrappers/CubeProjection.js.map +1 -0
- package/dist/unwrappers/CylinderProjection.d.ts +25 -0
- package/dist/unwrappers/CylinderProjection.d.ts.map +1 -0
- package/dist/unwrappers/CylinderProjection.js +50 -0
- package/dist/unwrappers/CylinderProjection.js.map +1 -0
- package/dist/unwrappers/SmartUVProject.d.ts +30 -0
- package/dist/unwrappers/SmartUVProject.d.ts.map +1 -0
- package/dist/unwrappers/SmartUVProject.js +212 -0
- package/dist/unwrappers/SmartUVProject.js.map +1 -0
- package/dist/unwrappers/SphereProjection.d.ts +24 -0
- package/dist/unwrappers/SphereProjection.d.ts.map +1 -0
- package/dist/unwrappers/SphereProjection.js +40 -0
- package/dist/unwrappers/SphereProjection.js.map +1 -0
- package/dist/utils/linearSolver.d.ts +19 -0
- package/dist/utils/linearSolver.d.ts.map +1 -0
- package/dist/utils/linearSolver.js +73 -0
- package/dist/utils/linearSolver.js.map +1 -0
- package/dist/utils/meshGraph.d.ts +71 -0
- package/dist/utils/meshGraph.d.ts.map +1 -0
- package/dist/utils/meshGraph.js +315 -0
- package/dist/utils/meshGraph.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { BufferAttribute } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
/**
|
|
4
|
+
* SmartUVProject — Blender UV: Smart UV Project
|
|
5
|
+
*
|
|
6
|
+
* Clusters faces by normal direction (within angleLimit degrees),
|
|
7
|
+
* projects each cluster onto its average normal plane,
|
|
8
|
+
* then packs all islands into UV [0,1]² space.
|
|
9
|
+
*
|
|
10
|
+
* Good for hard-surface models with mixed orientations.
|
|
11
|
+
*/
|
|
12
|
+
export class SmartUVProject extends BaseUnwrapper {
|
|
13
|
+
constructor(opts = {}) {
|
|
14
|
+
super();
|
|
15
|
+
this.unwrapType = 'SmartUVProject';
|
|
16
|
+
this.parameters = {
|
|
17
|
+
angleLimit: opts.angleLimit ?? 66,
|
|
18
|
+
islandMargin: opts.islandMargin ?? 0.02,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
apply(geometry) {
|
|
22
|
+
if (!this.enabled)
|
|
23
|
+
return geometry.clone();
|
|
24
|
+
// De-index so every triangle vertex gets its own UV slot.
|
|
25
|
+
// Shared vertices in indexed geometry would get conflicting UVs
|
|
26
|
+
// when they sit on a boundary between two different projection islands.
|
|
27
|
+
const result = geometry.index ? geometry.toNonIndexed() : geometry.clone();
|
|
28
|
+
const posAttr = result.getAttribute('position');
|
|
29
|
+
const count = posAttr.count;
|
|
30
|
+
if (count % 3 !== 0)
|
|
31
|
+
return geometry.clone();
|
|
32
|
+
const angleRad = (this.parameters.angleLimit * Math.PI) / 180;
|
|
33
|
+
const cosLimit = Math.cos(angleRad);
|
|
34
|
+
const faceNormals = [];
|
|
35
|
+
// After toNonIndexed() there is no index buffer — always use flat layout
|
|
36
|
+
const getTriIndices = (t) => {
|
|
37
|
+
if (result.index) {
|
|
38
|
+
const idx = result.index.array;
|
|
39
|
+
return [idx[t * 3], idx[t * 3 + 1], idx[t * 3 + 2]];
|
|
40
|
+
}
|
|
41
|
+
return [t * 3, t * 3 + 1, t * 3 + 2];
|
|
42
|
+
};
|
|
43
|
+
const numTriangles = Math.floor(count / 3);
|
|
44
|
+
for (let t = 0; t < numTriangles; t++) {
|
|
45
|
+
const [i, j, k] = getTriIndices(t);
|
|
46
|
+
const ax = posAttr.getX(i), ay = posAttr.getY(i), az = posAttr.getZ(i);
|
|
47
|
+
const bx = posAttr.getX(j), by = posAttr.getY(j), bz = posAttr.getZ(j);
|
|
48
|
+
const cx = posAttr.getX(k), cy = posAttr.getY(k), cz = posAttr.getZ(k);
|
|
49
|
+
const ex = bx - ax, ey = by - ay, ez = bz - az;
|
|
50
|
+
const fx = cx - ax, fy = cy - ay, fz = cz - az;
|
|
51
|
+
let nx = ey * fz - ez * fy;
|
|
52
|
+
let ny = ez * fx - ex * fz;
|
|
53
|
+
let nz = ex * fy - ey * fx;
|
|
54
|
+
const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
55
|
+
nx /= len;
|
|
56
|
+
ny /= len;
|
|
57
|
+
nz /= len;
|
|
58
|
+
faceNormals.push({ nx, ny, nz });
|
|
59
|
+
}
|
|
60
|
+
// Step 2: greedy cluster faces into islands by normal similarity
|
|
61
|
+
const faceIsland = new Int32Array(numTriangles).fill(-1);
|
|
62
|
+
const islandNormals = [];
|
|
63
|
+
const islands = [];
|
|
64
|
+
for (let t = 0; t < numTriangles; t++) {
|
|
65
|
+
if (faceIsland[t] !== -1)
|
|
66
|
+
continue;
|
|
67
|
+
const fn = faceNormals[t];
|
|
68
|
+
// Find nearest existing island within angle limit
|
|
69
|
+
let bestIsland = -1;
|
|
70
|
+
let bestDot = cosLimit;
|
|
71
|
+
for (let g = 0; g < islandNormals.length; g++) {
|
|
72
|
+
const gn = islandNormals[g];
|
|
73
|
+
const dot = fn.nx * gn.nx + fn.ny * gn.ny + fn.nz * gn.nz;
|
|
74
|
+
if (dot > bestDot) {
|
|
75
|
+
bestDot = dot;
|
|
76
|
+
bestIsland = g;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (bestIsland === -1) {
|
|
80
|
+
bestIsland = islands.length;
|
|
81
|
+
islands.push([]);
|
|
82
|
+
islandNormals.push({ ...fn });
|
|
83
|
+
}
|
|
84
|
+
faceIsland[t] = bestIsland;
|
|
85
|
+
islands[bestIsland].push(t);
|
|
86
|
+
// Update island average normal (running mean)
|
|
87
|
+
const gn = islandNormals[bestIsland];
|
|
88
|
+
const n = islands[bestIsland].length;
|
|
89
|
+
gn.nx = (gn.nx * (n - 1) + fn.nx) / n;
|
|
90
|
+
gn.ny = (gn.ny * (n - 1) + fn.ny) / n;
|
|
91
|
+
gn.nz = (gn.nz * (n - 1) + fn.nz) / n;
|
|
92
|
+
const len2 = Math.sqrt(gn.nx * gn.nx + gn.ny * gn.ny + gn.nz * gn.nz) || 1;
|
|
93
|
+
gn.nx /= len2;
|
|
94
|
+
gn.ny /= len2;
|
|
95
|
+
gn.nz /= len2;
|
|
96
|
+
}
|
|
97
|
+
const patches = islands.map((facesInIsland, g) => {
|
|
98
|
+
const gn = islandNormals[g];
|
|
99
|
+
// Build tangent frame for this island's normal
|
|
100
|
+
let tx = 1, ty = 0, tz = 0;
|
|
101
|
+
if (Math.abs(gn.nx) > 0.9) {
|
|
102
|
+
tx = 0;
|
|
103
|
+
ty = 1;
|
|
104
|
+
tz = 0;
|
|
105
|
+
}
|
|
106
|
+
// bitangent = normal × tangent
|
|
107
|
+
let bx = gn.ny * tz - gn.nz * ty;
|
|
108
|
+
let by = gn.nz * tx - gn.nx * tz;
|
|
109
|
+
let bz = gn.nx * ty - gn.ny * tx;
|
|
110
|
+
// re-orthogonalize tangent
|
|
111
|
+
tx = by * gn.nz - bz * gn.ny;
|
|
112
|
+
ty = bz * gn.nx - bx * gn.nz;
|
|
113
|
+
tz = bx * gn.ny - by * gn.nx;
|
|
114
|
+
const tLen = Math.sqrt(tx * tx + ty * ty + tz * tz) || 1;
|
|
115
|
+
const bLen = Math.sqrt(bx * bx + by * by + bz * bz) || 1;
|
|
116
|
+
tx /= tLen;
|
|
117
|
+
ty /= tLen;
|
|
118
|
+
tz /= tLen;
|
|
119
|
+
bx /= bLen;
|
|
120
|
+
by /= bLen;
|
|
121
|
+
bz /= bLen;
|
|
122
|
+
let minU = Infinity, maxU = -Infinity;
|
|
123
|
+
let minV = Infinity, maxV = -Infinity;
|
|
124
|
+
const faceUVs = facesInIsland.map(t => {
|
|
125
|
+
const [i, j, k] = getTriIndices(t);
|
|
126
|
+
return [i, j, k].map(vi => {
|
|
127
|
+
const px = posAttr.getX(vi), py = posAttr.getY(vi), pz = posAttr.getZ(vi);
|
|
128
|
+
const u = px * tx + py * ty + pz * tz;
|
|
129
|
+
const v = px * bx + py * by + pz * bz;
|
|
130
|
+
if (u < minU)
|
|
131
|
+
minU = u;
|
|
132
|
+
if (u > maxU)
|
|
133
|
+
maxU = u;
|
|
134
|
+
if (v < minV)
|
|
135
|
+
minV = v;
|
|
136
|
+
if (v > maxV)
|
|
137
|
+
maxV = v;
|
|
138
|
+
return [u, v];
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
return { faceUVs, minU, maxU, minV, maxV };
|
|
142
|
+
});
|
|
143
|
+
// Step 4: shelf-pack islands, then rescale to fill [0,1]²
|
|
144
|
+
const margin = this.parameters.islandMargin;
|
|
145
|
+
const totalArea = patches.reduce((s, p) => s + (p.maxU - p.minU) * (p.maxV - p.minV), 0);
|
|
146
|
+
const sqrtArea = Math.sqrt(totalArea) || 1;
|
|
147
|
+
const rects = patches.map((p, idx) => ({
|
|
148
|
+
x: 0, y: 0,
|
|
149
|
+
w: (p.maxU - p.minU) / sqrtArea,
|
|
150
|
+
h: (p.maxV - p.minV) / sqrtArea,
|
|
151
|
+
patchIdx: idx,
|
|
152
|
+
}));
|
|
153
|
+
// Sort by height descending for better shelf utilisation
|
|
154
|
+
rects.sort((a, b) => b.h - a.h);
|
|
155
|
+
let curX = 0, curY = 0, rowH = 0;
|
|
156
|
+
for (const rect of rects) {
|
|
157
|
+
if (curX + rect.w + margin > 1.0) {
|
|
158
|
+
curX = 0;
|
|
159
|
+
curY += rowH + margin;
|
|
160
|
+
rowH = 0;
|
|
161
|
+
}
|
|
162
|
+
rect.x = curX;
|
|
163
|
+
rect.y = curY;
|
|
164
|
+
if (rect.h > rowH)
|
|
165
|
+
rowH = rect.h;
|
|
166
|
+
curX += rect.w + margin;
|
|
167
|
+
}
|
|
168
|
+
// Rescale the packed layout so it fills exactly [0, 1-margin]²
|
|
169
|
+
// without this step islands can overflow [0,1] when sqrtArea < total packed extent
|
|
170
|
+
let packMaxX = 0, packMaxY = 0;
|
|
171
|
+
for (const r of rects) {
|
|
172
|
+
if (r.x + r.w > packMaxX)
|
|
173
|
+
packMaxX = r.x + r.w;
|
|
174
|
+
if (r.y + r.h > packMaxY)
|
|
175
|
+
packMaxY = r.y + r.h;
|
|
176
|
+
}
|
|
177
|
+
const scaleX = packMaxX > 0 ? (1 - margin) / packMaxX : 1;
|
|
178
|
+
const scaleY = packMaxY > 0 ? (1 - margin) / packMaxY : 1;
|
|
179
|
+
const packScale = Math.min(scaleX, scaleY); // uniform scale keeps aspect ratios
|
|
180
|
+
for (const r of rects) {
|
|
181
|
+
r.x *= packScale;
|
|
182
|
+
r.y *= packScale;
|
|
183
|
+
r.w *= packScale;
|
|
184
|
+
r.h *= packScale;
|
|
185
|
+
}
|
|
186
|
+
// Step 5: write UVs
|
|
187
|
+
const uv = new Float32Array(count * 2);
|
|
188
|
+
for (const rect of rects) {
|
|
189
|
+
const p = patches[rect.patchIdx];
|
|
190
|
+
const pW = p.maxU - p.minU || 1;
|
|
191
|
+
const pH = p.maxV - p.minV || 1;
|
|
192
|
+
const facesInIsland = islands[rect.patchIdx];
|
|
193
|
+
facesInIsland.forEach((t, fi) => {
|
|
194
|
+
const [i, j, k] = getTriIndices(t);
|
|
195
|
+
[i, j, k].forEach((vi, vl) => {
|
|
196
|
+
const [pu, pv] = p.faceUVs[fi][vl];
|
|
197
|
+
const u = rect.x + ((pu - p.minU) / pW) * rect.w;
|
|
198
|
+
const v = rect.y + ((pv - p.minV) / pH) * rect.h;
|
|
199
|
+
uv[vi * 2] = u;
|
|
200
|
+
uv[vi * 2 + 1] = v;
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
result.setAttribute('uv', new BufferAttribute(uv, 2));
|
|
205
|
+
// Recompute smooth normals — toNonIndexed() duplicates vertices so
|
|
206
|
+
// the original smooth normals are preserved, but calling this ensures
|
|
207
|
+
// correct per-face-vertex normals if the source had none.
|
|
208
|
+
result.computeVertexNormals();
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=SmartUVProject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartUVProject.js","sourceRoot":"","sources":["../../src/unwrappers/SmartUVProject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BufferGeometry } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
export interface SphereProjectionOptions {
|
|
4
|
+
/** UV scale. Default 1.0. Blender: Sphere Projection → Scale */
|
|
5
|
+
scale?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* SphereProjection — Blender UV: Sphere Projection
|
|
9
|
+
*
|
|
10
|
+
* Latitude/longitude spherical mapping.
|
|
11
|
+
* U = longitude (angle around Y-axis) → [0, 1].
|
|
12
|
+
* V = latitude (angle from top) → [0, 1].
|
|
13
|
+
*
|
|
14
|
+
* Best for spherical or organic shapes centered near the origin.
|
|
15
|
+
*/
|
|
16
|
+
export declare class SphereProjection extends BaseUnwrapper {
|
|
17
|
+
readonly unwrapType = "SphereProjection";
|
|
18
|
+
parameters: {
|
|
19
|
+
scale: number;
|
|
20
|
+
};
|
|
21
|
+
constructor(opts?: SphereProjectionOptions);
|
|
22
|
+
apply(geometry: BufferGeometry): BufferGeometry;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=SphereProjection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SphereProjection.d.ts","sourceRoot":"","sources":["../../src/unwrappers/SphereProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAExD,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,SAAQ,aAAa;IACjD,QAAQ,CAAC,UAAU,sBAAqB;IACxC,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;gBAEjB,IAAI,GAAE,uBAA4B;IAK9C,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc;CAyBhD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { BufferAttribute } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
/**
|
|
4
|
+
* SphereProjection — Blender UV: Sphere Projection
|
|
5
|
+
*
|
|
6
|
+
* Latitude/longitude spherical mapping.
|
|
7
|
+
* U = longitude (angle around Y-axis) → [0, 1].
|
|
8
|
+
* V = latitude (angle from top) → [0, 1].
|
|
9
|
+
*
|
|
10
|
+
* Best for spherical or organic shapes centered near the origin.
|
|
11
|
+
*/
|
|
12
|
+
export class SphereProjection extends BaseUnwrapper {
|
|
13
|
+
constructor(opts = {}) {
|
|
14
|
+
super();
|
|
15
|
+
this.unwrapType = 'SphereProjection';
|
|
16
|
+
this.parameters = { scale: opts.scale ?? 1.0 };
|
|
17
|
+
}
|
|
18
|
+
apply(geometry) {
|
|
19
|
+
if (!this.enabled)
|
|
20
|
+
return geometry.clone();
|
|
21
|
+
const result = geometry.clone();
|
|
22
|
+
const posAttr = result.getAttribute('position');
|
|
23
|
+
const count = posAttr.count;
|
|
24
|
+
const scale = this.parameters.scale;
|
|
25
|
+
const uv = new Float32Array(count * 2);
|
|
26
|
+
for (let i = 0; i < count; i++) {
|
|
27
|
+
const x = posAttr.getX(i);
|
|
28
|
+
const y = posAttr.getY(i);
|
|
29
|
+
const z = posAttr.getZ(i);
|
|
30
|
+
const r = Math.sqrt(x * x + y * y + z * z) || 1;
|
|
31
|
+
const u = (Math.atan2(x, z) / (2 * Math.PI) + 0.5) / scale;
|
|
32
|
+
const v = (Math.acos(Math.max(-1, Math.min(1, y / r))) / Math.PI) / scale;
|
|
33
|
+
uv[i * 2] = u;
|
|
34
|
+
uv[i * 2 + 1] = v;
|
|
35
|
+
}
|
|
36
|
+
result.setAttribute('uv', new BufferAttribute(uv, 2));
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=SphereProjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SphereProjection.js","sourceRoot":"","sources":["../../src/unwrappers/SphereProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAE,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAOxD;;;;;;;;GAQG;AACH,MAAM,OAAO,gBAAiB,SAAQ,aAAa;IAIjD,YAAY,OAAgC,EAAE;QAC5C,KAAK,EAAE,CAAA;QAJA,eAAU,GAAG,kBAAkB,CAAA;QAKtC,IAAI,CAAC,UAAU,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,CAAA;IAChD,CAAC;IAED,KAAK,CAAC,QAAwB;QAC5B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC,KAAK,EAAE,CAAA;QAE1C,MAAM,MAAM,GAAI,QAAQ,CAAC,KAAK,EAAE,CAAA;QAChC,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC,UAAU,CAAoB,CAAA;QAClE,MAAM,KAAK,GAAK,OAAO,CAAC,KAAK,CAAA;QAC7B,MAAM,KAAK,GAAK,IAAI,CAAC,UAAU,CAAC,KAAK,CAAA;QACrC,MAAM,EAAE,GAAQ,IAAI,YAAY,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAE3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACzB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACzB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACzB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;YAE/C,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAA;YAC1D,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAA;YAEzE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAO,CAAC,CAAA;YACjB,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;QACnB,CAAC;QAED,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,eAAe,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;QACrD,OAAO,MAAM,CAAA;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sparse conjugate gradient solver.
|
|
3
|
+
* Solves A*x = b where A is symmetric positive semi-definite.
|
|
4
|
+
* Used by LSCM and ABF for UV parameterization.
|
|
5
|
+
*
|
|
6
|
+
* Matrix stored as adjacency list: rows[i] = Map<column, value>
|
|
7
|
+
*/
|
|
8
|
+
export type SparseMatrix = Map<number, number>[];
|
|
9
|
+
/**
|
|
10
|
+
* Solve A*x = b using conjugate gradient.
|
|
11
|
+
* maxIter: maximum iterations (default 400)
|
|
12
|
+
* tol: convergence tolerance (default 1e-6)
|
|
13
|
+
*/
|
|
14
|
+
export declare function cgSolve(A: SparseMatrix, b: Float64Array, x0?: Float64Array, maxIter?: number, tol?: number): Float64Array;
|
|
15
|
+
/** Add value to sparse matrix entry (i, j). */
|
|
16
|
+
export declare function sparseAdd(A: SparseMatrix, i: number, j: number, val: number): void;
|
|
17
|
+
/** Create an n×n zero sparse matrix. */
|
|
18
|
+
export declare function sparseZero(n: number): SparseMatrix;
|
|
19
|
+
//# sourceMappingURL=linearSolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linearSolver.d.ts","sourceRoot":"","sources":["../../src/utils/linearSolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;AAmBhD;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,CAAC,EAAQ,YAAY,EACrB,CAAC,EAAQ,YAAY,EACrB,EAAE,CAAC,EAAM,YAAY,EACrB,OAAO,SAAM,EACb,GAAG,SAAW,GACb,YAAY,CAmCd;AAED,+CAA+C;AAC/C,wBAAgB,SAAS,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAGlF;AAED,wCAAwC;AACxC,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,YAAY,CAElD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sparse conjugate gradient solver.
|
|
3
|
+
* Solves A*x = b where A is symmetric positive semi-definite.
|
|
4
|
+
* Used by LSCM and ABF for UV parameterization.
|
|
5
|
+
*
|
|
6
|
+
* Matrix stored as adjacency list: rows[i] = Map<column, value>
|
|
7
|
+
*/
|
|
8
|
+
/** Multiply sparse matrix A by dense vector x, result into out. */
|
|
9
|
+
function spMv(A, x, out) {
|
|
10
|
+
const n = A.length;
|
|
11
|
+
for (let i = 0; i < n; i++) {
|
|
12
|
+
let sum = 0;
|
|
13
|
+
for (const [j, val] of A[i])
|
|
14
|
+
sum += val * x[j];
|
|
15
|
+
out[i] = sum;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Dot product of two dense vectors. */
|
|
19
|
+
function dot(a, b) {
|
|
20
|
+
let s = 0;
|
|
21
|
+
for (let i = 0; i < a.length; i++)
|
|
22
|
+
s += a[i] * b[i];
|
|
23
|
+
return s;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Solve A*x = b using conjugate gradient.
|
|
27
|
+
* maxIter: maximum iterations (default 400)
|
|
28
|
+
* tol: convergence tolerance (default 1e-6)
|
|
29
|
+
*/
|
|
30
|
+
export function cgSolve(A, b, x0, maxIter = 400, tol = 1e-6) {
|
|
31
|
+
const n = b.length;
|
|
32
|
+
const x = x0 ? new Float64Array(x0) : new Float64Array(n);
|
|
33
|
+
const r = new Float64Array(n);
|
|
34
|
+
const p = new Float64Array(n);
|
|
35
|
+
const Ap = new Float64Array(n);
|
|
36
|
+
// r = b - A*x
|
|
37
|
+
spMv(A, x, Ap);
|
|
38
|
+
for (let i = 0; i < n; i++)
|
|
39
|
+
r[i] = b[i] - Ap[i];
|
|
40
|
+
p.set(r);
|
|
41
|
+
let rsOld = dot(r, r);
|
|
42
|
+
if (Math.sqrt(rsOld) < tol)
|
|
43
|
+
return x;
|
|
44
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
45
|
+
spMv(A, p, Ap);
|
|
46
|
+
const pAp = dot(p, Ap);
|
|
47
|
+
if (Math.abs(pAp) < 1e-14)
|
|
48
|
+
break;
|
|
49
|
+
const alpha = rsOld / pAp;
|
|
50
|
+
for (let i = 0; i < n; i++) {
|
|
51
|
+
x[i] += alpha * p[i];
|
|
52
|
+
r[i] -= alpha * Ap[i];
|
|
53
|
+
}
|
|
54
|
+
const rsNew = dot(r, r);
|
|
55
|
+
if (Math.sqrt(rsNew) < tol)
|
|
56
|
+
break;
|
|
57
|
+
const beta = rsNew / rsOld;
|
|
58
|
+
for (let i = 0; i < n; i++)
|
|
59
|
+
p[i] = r[i] + beta * p[i];
|
|
60
|
+
rsOld = rsNew;
|
|
61
|
+
}
|
|
62
|
+
return x;
|
|
63
|
+
}
|
|
64
|
+
/** Add value to sparse matrix entry (i, j). */
|
|
65
|
+
export function sparseAdd(A, i, j, val) {
|
|
66
|
+
const existing = A[i].get(j) ?? 0;
|
|
67
|
+
A[i].set(j, existing + val);
|
|
68
|
+
}
|
|
69
|
+
/** Create an n×n zero sparse matrix. */
|
|
70
|
+
export function sparseZero(n) {
|
|
71
|
+
return Array.from({ length: n }, () => new Map());
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=linearSolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linearSolver.js","sourceRoot":"","sources":["../../src/utils/linearSolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BufferGeometry } from 'three';
|
|
2
|
+
export interface MeshGraph {
|
|
3
|
+
positions: Float32Array;
|
|
4
|
+
triangles: Uint32Array;
|
|
5
|
+
vertexCount: number;
|
|
6
|
+
triangleCount: number;
|
|
7
|
+
/** For each vertex: list of incident triangle indices */
|
|
8
|
+
vertexTriangles: number[][];
|
|
9
|
+
/** Edge "minV_maxV" -> triangle indices (1 or 2) */
|
|
10
|
+
edgeTriangles: Map<string, number[]>;
|
|
11
|
+
/** Vertex indices on the mesh boundary (edges with only 1 incident triangle) */
|
|
12
|
+
boundary: number[];
|
|
13
|
+
/** Set for fast boundary lookup */
|
|
14
|
+
boundarySet: Set<number>;
|
|
15
|
+
/**
|
|
16
|
+
* Set to true by applySeamCuts when vertices are duplicated along seams.
|
|
17
|
+
* Tells applyUVsToGeometry to de-index the output so each triangle corner
|
|
18
|
+
* gets its own UV slot (required for different UVs on each side of a seam).
|
|
19
|
+
*/
|
|
20
|
+
hasSeamSplits?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build a MeshGraph from a BufferGeometry.
|
|
24
|
+
* Handles both indexed and non-indexed geometry.
|
|
25
|
+
* For non-indexed geometry, merges vertices at identical positions.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildMeshGraph(geometry: BufferGeometry): MeshGraph;
|
|
28
|
+
/** Get the index of the vertex opposite to edge (a,b) in triangle t. */
|
|
29
|
+
export declare function oppositeVertex(triangles: Uint32Array, t: number, a: number, b: number): number;
|
|
30
|
+
/** Cotangent of angle at vertex opp in the triangle opp-a-b. */
|
|
31
|
+
export declare function cotangentWeight(positions: Float32Array, opp: number, a: number, b: number): number;
|
|
32
|
+
/** Mean-value weight for edge (i,j) at vertex i — used by ABF. */
|
|
33
|
+
export declare function meanValueWeight(positions: Float32Array, i: number, j: number, opp: number): number;
|
|
34
|
+
/**
|
|
35
|
+
* Apply seam cuts to a MeshGraph in-place.
|
|
36
|
+
*
|
|
37
|
+
* For each seam edge (a,b) that has two incident triangles [t0, t1]:
|
|
38
|
+
* - Duplicates vertices a and b as new vertices a' and b'
|
|
39
|
+
* - Rewires triangle t1 to use a' and b' instead of a and b
|
|
40
|
+
* - Removes the shared edge and adds two free boundary edges instead
|
|
41
|
+
* - Updates all adjacency structures (vertexTriangles, edgeTriangles, positions)
|
|
42
|
+
*
|
|
43
|
+
* After all cuts, recomputes boundary / boundarySet so the solver treats the
|
|
44
|
+
* seam edges as open boundaries and assigns independent UV coordinates on each side.
|
|
45
|
+
*
|
|
46
|
+
* Called by ConformalLSCM and AngleBasedABF when geometry.userData.seams is set.
|
|
47
|
+
*/
|
|
48
|
+
export declare function applySeamCuts(graph: MeshGraph, seams: Array<{
|
|
49
|
+
a: number;
|
|
50
|
+
b: number;
|
|
51
|
+
}>): void;
|
|
52
|
+
/**
|
|
53
|
+
* Order boundary vertices into a closed loop.
|
|
54
|
+
* Returns undefined if boundary is empty or has complex topology.
|
|
55
|
+
*/
|
|
56
|
+
export declare function orderBoundaryLoop(graph: MeshGraph): number[] | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Copy the UV attribute from uvArray back into a clone of the source geometry.
|
|
59
|
+
* uvArray: Float32Array of length graph.vertexCount * 2
|
|
60
|
+
*
|
|
61
|
+
* Normal case (no seam splits):
|
|
62
|
+
* - Indexed geometry: write UVs per unique vertex index.
|
|
63
|
+
* - Non-indexed geometry: expand UVs back to per-face-vertex layout.
|
|
64
|
+
*
|
|
65
|
+
* Seam-split case (graph.hasSeamSplits === true):
|
|
66
|
+
* The graph has duplicated vertices; graph.triangles maps each triangle corner
|
|
67
|
+
* to a graph vertex index. We de-index the output so that each face-vertex
|
|
68
|
+
* gets its own UV slot, enabling different UVs on each side of the seam.
|
|
69
|
+
*/
|
|
70
|
+
export declare function applyUVsToGeometry(source: BufferGeometry, uvArray: Float32Array, graph: MeshGraph): BufferGeometry;
|
|
71
|
+
//# sourceMappingURL=meshGraph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meshGraph.d.ts","sourceRoot":"","sources":["../../src/utils/meshGraph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,OAAO,CAAA;AAEvD,MAAM,WAAW,SAAS;IACxB,SAAS,EAAQ,YAAY,CAAA;IAC7B,SAAS,EAAQ,WAAW,CAAA;IAC5B,WAAW,EAAM,MAAM,CAAA;IACvB,aAAa,EAAI,MAAM,CAAA;IACvB,yDAAyD;IACzD,eAAe,EAAE,MAAM,EAAE,EAAE,CAAA;IAC3B,oDAAoD;IACpD,aAAa,EAAI,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACtC,gFAAgF;IAChF,QAAQ,EAAS,MAAM,EAAE,CAAA;IACzB,mCAAmC;IACnC,WAAW,EAAM,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAG,OAAO,CAAA;CACzB;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,CA0ElE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAK9F;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBlG;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,SAAS,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAelG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACrC,IAAI,CAsFN;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS,CA+BxE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,SAAS,GACf,cAAc,CAwDhB"}
|