@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,162 @@
|
|
|
1
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
2
|
+
import { buildMeshGraph, orderBoundaryLoop, applyUVsToGeometry, meanValueWeight, oppositeVertex, applySeamCuts, } from '../utils/meshGraph.js';
|
|
3
|
+
import { cgSolve, sparseAdd, sparseZero } from '../utils/linearSolver.js';
|
|
4
|
+
/**
|
|
5
|
+
* AngleBasedABF — Blender UV: Unwrap (Angle Based)
|
|
6
|
+
*
|
|
7
|
+
* Angle-Based Flattening approximation using mean-value coordinates.
|
|
8
|
+
* Minimises angle distortion better than LSCM for meshes with obtuse triangles.
|
|
9
|
+
* Uses mean-value weights instead of cotangent weights — always positive,
|
|
10
|
+
* so it handles obtuse triangles without negative weights.
|
|
11
|
+
*
|
|
12
|
+
* Works best on meshes with a natural boundary (disc topology).
|
|
13
|
+
* For closed meshes, a seam is automatically inserted between the two
|
|
14
|
+
* farthest-apart vertices.
|
|
15
|
+
*/
|
|
16
|
+
export class AngleBasedABF extends BaseUnwrapper {
|
|
17
|
+
constructor(opts = {}) {
|
|
18
|
+
super();
|
|
19
|
+
this.unwrapType = 'AngleBasedABF';
|
|
20
|
+
this.parameters = {
|
|
21
|
+
maxIterations: opts.maxIterations ?? 400,
|
|
22
|
+
tolerance: opts.tolerance ?? 1e-6,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
apply(geometry) {
|
|
26
|
+
if (!this.enabled)
|
|
27
|
+
return geometry.clone();
|
|
28
|
+
const graph = buildMeshGraph(geometry);
|
|
29
|
+
// Apply seam cuts before solving — treats seam edges as boundary
|
|
30
|
+
const seams = geometry.userData?.seams;
|
|
31
|
+
if (seams && seams.length > 0)
|
|
32
|
+
applySeamCuts(graph, seams);
|
|
33
|
+
const { positions, triangles, vertexCount, triangleCount, edgeTriangles } = graph;
|
|
34
|
+
if (vertexCount < 3 || triangleCount < 1)
|
|
35
|
+
return geometry.clone();
|
|
36
|
+
const { maxIterations, tolerance } = this.parameters;
|
|
37
|
+
// Identify pinned vertices
|
|
38
|
+
let pin0, pin1;
|
|
39
|
+
const boundaryLoop = orderBoundaryLoop(graph);
|
|
40
|
+
if (boundaryLoop && boundaryLoop.length >= 2) {
|
|
41
|
+
pin0 = boundaryLoop[0];
|
|
42
|
+
pin1 = boundaryLoop[Math.floor(boundaryLoop.length / 2)];
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
pin0 = 0;
|
|
46
|
+
let maxDist = 0;
|
|
47
|
+
pin1 = 1;
|
|
48
|
+
for (let i = 1; i < vertexCount; i++) {
|
|
49
|
+
const dx = positions[i * 3] - positions[0];
|
|
50
|
+
const dy = positions[i * 3 + 1] - positions[1];
|
|
51
|
+
const dz = positions[i * 3 + 2] - positions[2];
|
|
52
|
+
const d = dx * dx + dy * dy + dz * dz;
|
|
53
|
+
if (d > maxDist) {
|
|
54
|
+
maxDist = d;
|
|
55
|
+
pin1 = i;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Build mean-value weight map: always positive, handles obtuse triangles
|
|
60
|
+
const weights = Array.from({ length: vertexCount }, () => new Map());
|
|
61
|
+
for (const [key, tris] of edgeTriangles) {
|
|
62
|
+
const [a, b] = key.split('_').map(Number);
|
|
63
|
+
let w = 0;
|
|
64
|
+
for (const t of tris) {
|
|
65
|
+
const opp = oppositeVertex(triangles, t, a, b);
|
|
66
|
+
// Mean-value weight at vertex a for edge a→b
|
|
67
|
+
w += meanValueWeight(positions, a, b, opp);
|
|
68
|
+
}
|
|
69
|
+
w = Math.max(1e-6, w);
|
|
70
|
+
const wa = (weights[a].get(b) ?? 0) + w;
|
|
71
|
+
weights[a].set(b, wa);
|
|
72
|
+
// Symmetric: weight at b for edge b→a
|
|
73
|
+
let wb2 = 0;
|
|
74
|
+
for (const t of tris) {
|
|
75
|
+
const opp = oppositeVertex(triangles, t, a, b);
|
|
76
|
+
wb2 += meanValueWeight(positions, b, a, opp);
|
|
77
|
+
}
|
|
78
|
+
wb2 = Math.max(1e-6, wb2);
|
|
79
|
+
const wb = (weights[b].get(a) ?? 0) + wb2;
|
|
80
|
+
weights[b].set(a, wb);
|
|
81
|
+
}
|
|
82
|
+
// Map free vertex indices
|
|
83
|
+
const freeIdx = new Int32Array(vertexCount).fill(-1);
|
|
84
|
+
let numFree = 0;
|
|
85
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
86
|
+
if (i !== pin0 && i !== pin1)
|
|
87
|
+
freeIdx[i] = numFree++;
|
|
88
|
+
}
|
|
89
|
+
if (numFree === 0) {
|
|
90
|
+
const uvs = new Float32Array(vertexCount * 2);
|
|
91
|
+
uvs[pin0 * 2] = 0;
|
|
92
|
+
uvs[pin0 * 2 + 1] = 0;
|
|
93
|
+
uvs[pin1 * 2] = 1;
|
|
94
|
+
uvs[pin1 * 2 + 1] = 0;
|
|
95
|
+
return applyUVsToGeometry(geometry, uvs, graph);
|
|
96
|
+
}
|
|
97
|
+
const AU = sparseZero(numFree);
|
|
98
|
+
const AV = sparseZero(numFree);
|
|
99
|
+
const bU = new Float64Array(numFree);
|
|
100
|
+
const bV = new Float64Array(numFree);
|
|
101
|
+
const pinUV = new Map([
|
|
102
|
+
[pin0, [0, 0]],
|
|
103
|
+
[pin1, [1, 0]],
|
|
104
|
+
]);
|
|
105
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
106
|
+
const fi = freeIdx[i];
|
|
107
|
+
if (fi < 0)
|
|
108
|
+
continue;
|
|
109
|
+
let diagSum = 0;
|
|
110
|
+
for (const [j, w] of weights[i]) {
|
|
111
|
+
diagSum += w;
|
|
112
|
+
const fj = freeIdx[j];
|
|
113
|
+
if (fj >= 0) {
|
|
114
|
+
sparseAdd(AU, fi, fj, -w);
|
|
115
|
+
sparseAdd(AV, fi, fj, -w);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const [pu, pv] = pinUV.get(j) ?? [0, 0];
|
|
119
|
+
bU[fi] += w * pu;
|
|
120
|
+
bV[fi] += w * pv;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
sparseAdd(AU, fi, fi, diagSum);
|
|
124
|
+
sparseAdd(AV, fi, fi, diagSum);
|
|
125
|
+
}
|
|
126
|
+
const solU = cgSolve(AU, bU, undefined, maxIterations, tolerance);
|
|
127
|
+
const solV = cgSolve(AV, bV, undefined, maxIterations, tolerance);
|
|
128
|
+
const uvs = new Float32Array(vertexCount * 2);
|
|
129
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
130
|
+
const fi = freeIdx[i];
|
|
131
|
+
if (fi >= 0) {
|
|
132
|
+
uvs[i * 2] = solU[fi];
|
|
133
|
+
uvs[i * 2 + 1] = solV[fi];
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const [pu, pv] = pinUV.get(i) ?? [0, 0];
|
|
137
|
+
uvs[i * 2] = pu;
|
|
138
|
+
uvs[i * 2 + 1] = pv;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Remap to [0,1]
|
|
142
|
+
let minU = Infinity, maxU = -Infinity, minV = Infinity, maxV = -Infinity;
|
|
143
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
144
|
+
const u = uvs[i * 2], v = uvs[i * 2 + 1];
|
|
145
|
+
if (u < minU)
|
|
146
|
+
minU = u;
|
|
147
|
+
if (u > maxU)
|
|
148
|
+
maxU = u;
|
|
149
|
+
if (v < minV)
|
|
150
|
+
minV = v;
|
|
151
|
+
if (v > maxV)
|
|
152
|
+
maxV = v;
|
|
153
|
+
}
|
|
154
|
+
const rangeU = maxU - minU || 1, rangeV = maxV - minV || 1;
|
|
155
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
156
|
+
uvs[i * 2] = (uvs[i * 2] - minU) / rangeU;
|
|
157
|
+
uvs[i * 2 + 1] = (uvs[i * 2 + 1] - minV) / rangeV;
|
|
158
|
+
}
|
|
159
|
+
return applyUVsToGeometry(geometry, uvs, graph);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=AngleBasedABF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AngleBasedABF.js","sourceRoot":"","sources":["../../src/unwrappers/AngleBasedABF.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EACL,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EACrD,eAAe,EAAE,cAAc,EAAE,aAAa,GAC/C,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AASzE;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,aAAc,SAAQ,aAAa;IAI9C,YAAY,OAA6B,EAAE;QACzC,KAAK,EAAE,CAAA;QAJA,eAAU,GAAG,eAAe,CAAA;QAKnC,IAAI,CAAC,UAAU,GAAG;YAChB,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,GAAG;YACxC,SAAS,EAAM,IAAI,CAAC,SAAS,IAAQ,IAAI;SAC1C,CAAA;IACH,CAAC;IAED,KAAK,CAAC,QAAwB;QAC5B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC,KAAK,EAAE,CAAA;QAE1C,MAAM,KAAK,GAAI,cAAc,CAAC,QAAQ,CAAC,CAAA;QAEvC,iEAAiE;QACjE,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,KAAoD,CAAA;QACrF,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QAE1D,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,KAAK,CAAA;QAEjF,IAAI,WAAW,GAAG,CAAC,IAAI,aAAa,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC,KAAK,EAAE,CAAA;QAEjE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,UAAU,CAAA;QAEpD,2BAA2B;QAC3B,IAAI,IAAY,EAAE,IAAY,CAAA;QAC9B,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAA;QAE7C,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;YACtB,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAA;QAC1D,CAAC;aAAM,CAAC;YACN,IAAI,GAAG,CAAC,CAAA;YACR,IAAI,OAAO,GAAG,CAAC,CAAC;YAAC,IAAI,GAAG,CAAC,CAAA;YACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,GAAC,CAAC,CAAC,GAAK,SAAS,CAAC,CAAC,CAAC,CAAA;gBAC1C,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;gBAC1C,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,GAAC,CAAC,GAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;gBAC1C,MAAM,CAAC,GAAI,EAAE,GAAC,EAAE,GAAG,EAAE,GAAC,EAAE,GAAG,EAAE,GAAC,EAAE,CAAA;gBAChC,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC;oBAAC,OAAO,GAAG,CAAC,CAAC;oBAAC,IAAI,GAAG,CAAC,CAAA;gBAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,yEAAyE;QACzE,MAAM,OAAO,GAA0B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAA;QAE3F,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,aAAa,EAAE,CAAC;YACxC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YACzC,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;gBAC9C,6CAA6C;gBAC7C,CAAC,IAAI,eAAe,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;YAC5C,CAAC;YACD,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;YACrB,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;YACvC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;YAErB,sCAAsC;YACtC,IAAI,GAAG,GAAG,CAAC,CAAA;YACX,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;gBAC9C,GAAG,IAAI,eAAe,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;YAC9C,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;YACzB,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAA;YACzC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACvB,CAAC;QAED,0BAA0B;QAC1B,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QACpD,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;gBAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,CAAA;QACtD,CAAC;QAED,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;YAClB,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,WAAW,GAAG,CAAC,CAAC,CAAA;YAC7C,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;YACxC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;YACxC,OAAO,kBAAkB,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;QACjD,CAAC;QAED,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;QAC9B,MAAM,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;QAC9B,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,CAAA;QACpC,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,CAAA;QAEpC,MAAM,KAAK,GAAG,IAAI,GAAG,CAA2B;YAC9C,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACd,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SACf,CAAC,CAAA;QAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YACrB,IAAI,EAAE,GAAG,CAAC;gBAAE,SAAQ;YAEpB,IAAI,OAAO,GAAG,CAAC,CAAA;YACf,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChC,OAAO,IAAI,CAAC,CAAA;gBACZ,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;gBACrB,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;oBACZ,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;oBACzB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAA;gBAC3B,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;oBACvC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;oBAChB,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;gBAClB,CAAC;YACH,CAAC;YACD,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;YAC9B,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;QAChC,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,CAAC,CAAA;QACjE,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,CAAC,CAAA;QAEjE,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,WAAW,GAAG,CAAC,CAAC,CAAA;QAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;YACrB,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACZ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAO,IAAI,CAAC,EAAE,CAAC,CAAA;gBACzB,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YAC3B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;gBACvC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAO,EAAE,CAAA;gBACnB,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAA;YACrB,CAAC;QACH,CAAC;QAED,iBAAiB;QACjB,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAA;QACxE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;YACxC,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAC;YAAC,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAA;YAC9C,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAC;YAAC,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAA;QAChD,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,CAAA;QAC1D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAO,IAAI,CAAC,GAAG,MAAM,CAAA;YACjD,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,CAAA;QACnD,CAAC;QAED,OAAO,kBAAkB,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;IACjD,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BufferGeometry } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
export interface ConformalLSCMOptions {
|
|
4
|
+
/** Maximum solver iterations. Default 400. */
|
|
5
|
+
maxIterations?: number;
|
|
6
|
+
/** Solver convergence tolerance. Default 1e-6. */
|
|
7
|
+
tolerance?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* ConformalLSCM — Blender UV: Unwrap (Conformal / LSCM)
|
|
11
|
+
*
|
|
12
|
+
* Least Squares Conformal Maps parameterization.
|
|
13
|
+
* Minimises angular distortion — angles in the UV map match the 3D mesh.
|
|
14
|
+
* Uses cotangent-weighted Laplacian with boundary conditions.
|
|
15
|
+
*
|
|
16
|
+
* Works best on meshes with a natural boundary (disc topology).
|
|
17
|
+
* For closed meshes, a seam is automatically inserted between the two
|
|
18
|
+
* farthest-apart vertices.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ConformalLSCM extends BaseUnwrapper {
|
|
21
|
+
readonly unwrapType = "ConformalLSCM";
|
|
22
|
+
parameters: {
|
|
23
|
+
maxIterations: number;
|
|
24
|
+
tolerance: number;
|
|
25
|
+
};
|
|
26
|
+
constructor(opts?: ConformalLSCMOptions);
|
|
27
|
+
apply(geometry: BufferGeometry): BufferGeometry;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=ConformalLSCM.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConformalLSCM.d.ts","sourceRoot":"","sources":["../../src/unwrappers/ConformalLSCM.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAOxD,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,aAAc,SAAQ,aAAa;IAC9C,QAAQ,CAAC,UAAU,mBAAkB;IACrC,UAAU,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;gBAE5C,IAAI,GAAE,oBAAyB;IAQ3C,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc;CA6IhD"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
2
|
+
import { buildMeshGraph, orderBoundaryLoop, applyUVsToGeometry, cotangentWeight, oppositeVertex, applySeamCuts, } from '../utils/meshGraph.js';
|
|
3
|
+
import { cgSolve, sparseAdd, sparseZero } from '../utils/linearSolver.js';
|
|
4
|
+
/**
|
|
5
|
+
* ConformalLSCM — Blender UV: Unwrap (Conformal / LSCM)
|
|
6
|
+
*
|
|
7
|
+
* Least Squares Conformal Maps parameterization.
|
|
8
|
+
* Minimises angular distortion — angles in the UV map match the 3D mesh.
|
|
9
|
+
* Uses cotangent-weighted Laplacian with boundary conditions.
|
|
10
|
+
*
|
|
11
|
+
* Works best on meshes with a natural boundary (disc topology).
|
|
12
|
+
* For closed meshes, a seam is automatically inserted between the two
|
|
13
|
+
* farthest-apart vertices.
|
|
14
|
+
*/
|
|
15
|
+
export class ConformalLSCM extends BaseUnwrapper {
|
|
16
|
+
constructor(opts = {}) {
|
|
17
|
+
super();
|
|
18
|
+
this.unwrapType = 'ConformalLSCM';
|
|
19
|
+
this.parameters = {
|
|
20
|
+
maxIterations: opts.maxIterations ?? 400,
|
|
21
|
+
tolerance: opts.tolerance ?? 1e-6,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
apply(geometry) {
|
|
25
|
+
if (!this.enabled)
|
|
26
|
+
return geometry.clone();
|
|
27
|
+
const graph = buildMeshGraph(geometry);
|
|
28
|
+
// Apply seam cuts before solving — treats seam edges as boundary
|
|
29
|
+
const seams = geometry.userData?.seams;
|
|
30
|
+
if (seams && seams.length > 0)
|
|
31
|
+
applySeamCuts(graph, seams);
|
|
32
|
+
const { positions, triangles, vertexCount, triangleCount, edgeTriangles, vertexTriangles } = graph;
|
|
33
|
+
if (vertexCount < 3 || triangleCount < 1)
|
|
34
|
+
return geometry.clone();
|
|
35
|
+
// Build cotangent-weighted Laplacian system
|
|
36
|
+
// For each interior vertex i: sum_j w_ij * (u_j - u_i) = 0
|
|
37
|
+
// => sum_j w_ij * u_j - (sum_j w_ij) * u_i = 0
|
|
38
|
+
const { maxIterations, tolerance } = this.parameters;
|
|
39
|
+
// Identify pinned vertices (2 boundary vertices, or vertex 0 and farthest if no boundary)
|
|
40
|
+
let pin0, pin1;
|
|
41
|
+
const boundaryLoop = orderBoundaryLoop(graph);
|
|
42
|
+
if (boundaryLoop && boundaryLoop.length >= 2) {
|
|
43
|
+
pin0 = boundaryLoop[0];
|
|
44
|
+
pin1 = boundaryLoop[Math.floor(boundaryLoop.length / 2)];
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Closed mesh: pin vertex 0 and farthest vertex from it
|
|
48
|
+
pin0 = 0;
|
|
49
|
+
let maxDist = 0;
|
|
50
|
+
pin1 = 1;
|
|
51
|
+
for (let i = 1; i < vertexCount; i++) {
|
|
52
|
+
const dx = positions[i * 3] - positions[0];
|
|
53
|
+
const dy = positions[i * 3 + 1] - positions[1];
|
|
54
|
+
const dz = positions[i * 3 + 2] - positions[2];
|
|
55
|
+
const d = dx * dx + dy * dy + dz * dz;
|
|
56
|
+
if (d > maxDist) {
|
|
57
|
+
maxDist = d;
|
|
58
|
+
pin1 = i;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Build weight map: w[i][j] = cotangent weight for edge (i,j)
|
|
63
|
+
const weights = Array.from({ length: vertexCount }, () => new Map());
|
|
64
|
+
for (const [key, tris] of edgeTriangles) {
|
|
65
|
+
const [a, b] = key.split('_').map(Number);
|
|
66
|
+
let w = 0;
|
|
67
|
+
for (const t of tris) {
|
|
68
|
+
const opp = oppositeVertex(triangles, t, a, b);
|
|
69
|
+
w += cotangentWeight(positions, opp, a, b);
|
|
70
|
+
}
|
|
71
|
+
w = Math.max(0, w); // clamp negative cotangents (obtuse triangles)
|
|
72
|
+
const wa = (weights[a].get(b) ?? 0) + w;
|
|
73
|
+
const wb = (weights[b].get(a) ?? 0) + w;
|
|
74
|
+
weights[a].set(b, wa);
|
|
75
|
+
weights[b].set(a, wb);
|
|
76
|
+
}
|
|
77
|
+
// Map vertex indices to free indices (excluding pinned)
|
|
78
|
+
const freeIdx = new Int32Array(vertexCount).fill(-1);
|
|
79
|
+
let numFree = 0;
|
|
80
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
81
|
+
if (i !== pin0 && i !== pin1)
|
|
82
|
+
freeIdx[i] = numFree++;
|
|
83
|
+
}
|
|
84
|
+
if (numFree === 0) {
|
|
85
|
+
// Only 2 vertices — trivial
|
|
86
|
+
const uvs = new Float32Array(vertexCount * 2);
|
|
87
|
+
uvs[pin0 * 2] = 0;
|
|
88
|
+
uvs[pin0 * 2 + 1] = 0;
|
|
89
|
+
uvs[pin1 * 2] = 1;
|
|
90
|
+
uvs[pin1 * 2 + 1] = 0;
|
|
91
|
+
return applyUVsToGeometry(geometry, uvs, graph);
|
|
92
|
+
}
|
|
93
|
+
// Build separate U and V systems
|
|
94
|
+
// A * u_free = b_u (contributions from pinned vertices go to RHS)
|
|
95
|
+
const AU = sparseZero(numFree);
|
|
96
|
+
const AV = sparseZero(numFree);
|
|
97
|
+
const bU = new Float64Array(numFree);
|
|
98
|
+
const bV = new Float64Array(numFree);
|
|
99
|
+
// Pin UV values: pin0 → (0, 0), pin1 → (1, 0)
|
|
100
|
+
const pinUV = new Map([
|
|
101
|
+
[pin0, [0, 0]],
|
|
102
|
+
[pin1, [1, 0]],
|
|
103
|
+
]);
|
|
104
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
105
|
+
const fi = freeIdx[i];
|
|
106
|
+
if (fi < 0)
|
|
107
|
+
continue; // pinned
|
|
108
|
+
let diagSum = 0;
|
|
109
|
+
for (const [j, w] of weights[i]) {
|
|
110
|
+
diagSum += w;
|
|
111
|
+
const fj = freeIdx[j];
|
|
112
|
+
if (fj >= 0) {
|
|
113
|
+
// Free neighbor → add to A
|
|
114
|
+
sparseAdd(AU, fi, fj, -w);
|
|
115
|
+
sparseAdd(AV, fi, fj, -w);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Pinned neighbor → add to RHS
|
|
119
|
+
const [pu, pv] = pinUV.get(j) ?? [0, 0];
|
|
120
|
+
bU[fi] += w * pu;
|
|
121
|
+
bV[fi] += w * pv;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
sparseAdd(AU, fi, fi, diagSum);
|
|
125
|
+
sparseAdd(AV, fi, fi, diagSum);
|
|
126
|
+
}
|
|
127
|
+
// Solve
|
|
128
|
+
const solU = cgSolve(AU, bU, undefined, maxIterations, tolerance);
|
|
129
|
+
const solV = cgSolve(AV, bV, undefined, maxIterations, tolerance);
|
|
130
|
+
// Reconstruct full UV array
|
|
131
|
+
const uvs = new Float32Array(vertexCount * 2);
|
|
132
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
133
|
+
const fi = freeIdx[i];
|
|
134
|
+
if (fi >= 0) {
|
|
135
|
+
uvs[i * 2] = solU[fi];
|
|
136
|
+
uvs[i * 2 + 1] = solV[fi];
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const [pu, pv] = pinUV.get(i) ?? [0, 0];
|
|
140
|
+
uvs[i * 2] = pu;
|
|
141
|
+
uvs[i * 2 + 1] = pv;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Remap to [0,1]
|
|
145
|
+
let minU = Infinity, maxU = -Infinity, minV = Infinity, maxV = -Infinity;
|
|
146
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
147
|
+
const u = uvs[i * 2], v = uvs[i * 2 + 1];
|
|
148
|
+
if (u < minU)
|
|
149
|
+
minU = u;
|
|
150
|
+
if (u > maxU)
|
|
151
|
+
maxU = u;
|
|
152
|
+
if (v < minV)
|
|
153
|
+
minV = v;
|
|
154
|
+
if (v > maxV)
|
|
155
|
+
maxV = v;
|
|
156
|
+
}
|
|
157
|
+
const rangeU = maxU - minU || 1, rangeV = maxV - minV || 1;
|
|
158
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
159
|
+
uvs[i * 2] = (uvs[i * 2] - minU) / rangeU;
|
|
160
|
+
uvs[i * 2 + 1] = (uvs[i * 2 + 1] - minV) / rangeV;
|
|
161
|
+
}
|
|
162
|
+
return applyUVsToGeometry(geometry, uvs, graph);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=ConformalLSCM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConformalLSCM.js","sourceRoot":"","sources":["../../src/unwrappers/ConformalLSCM.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BufferGeometry } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
export interface CubeProjectionOptions {
|
|
4
|
+
/** UV scale. Default 1.0. Blender: Cube Projection → Scale */
|
|
5
|
+
scale?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* CubeProjection — Blender UV: Cube Projection
|
|
9
|
+
*
|
|
10
|
+
* Projects UVs from the dominant face normal direction.
|
|
11
|
+
* Each vertex is mapped to one of 6 cube faces based on its normal.
|
|
12
|
+
* Good for blocky/hard-surface geometry with clear face orientations.
|
|
13
|
+
*/
|
|
14
|
+
export declare class CubeProjection extends BaseUnwrapper {
|
|
15
|
+
readonly unwrapType = "CubeProjection";
|
|
16
|
+
parameters: {
|
|
17
|
+
scale: number;
|
|
18
|
+
};
|
|
19
|
+
constructor(opts?: CubeProjectionOptions);
|
|
20
|
+
apply(geometry: BufferGeometry): BufferGeometry;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=CubeProjection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CubeProjection.d.ts","sourceRoot":"","sources":["../../src/unwrappers/CubeProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAExD,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,aAAa;IAC/C,QAAQ,CAAC,UAAU,oBAAmB;IACtC,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;gBAEjB,IAAI,GAAE,qBAA0B;IAK5C,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc;CA4ChD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { BufferAttribute } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
/**
|
|
4
|
+
* CubeProjection — Blender UV: Cube Projection
|
|
5
|
+
*
|
|
6
|
+
* Projects UVs from the dominant face normal direction.
|
|
7
|
+
* Each vertex is mapped to one of 6 cube faces based on its normal.
|
|
8
|
+
* Good for blocky/hard-surface geometry with clear face orientations.
|
|
9
|
+
*/
|
|
10
|
+
export class CubeProjection extends BaseUnwrapper {
|
|
11
|
+
constructor(opts = {}) {
|
|
12
|
+
super();
|
|
13
|
+
this.unwrapType = 'CubeProjection';
|
|
14
|
+
this.parameters = { scale: opts.scale ?? 1.0 };
|
|
15
|
+
}
|
|
16
|
+
apply(geometry) {
|
|
17
|
+
if (!this.enabled)
|
|
18
|
+
return geometry.clone();
|
|
19
|
+
const result = geometry.clone();
|
|
20
|
+
const posAttr = result.getAttribute('position');
|
|
21
|
+
const nrmAttr = result.getAttribute('normal');
|
|
22
|
+
const count = posAttr.count;
|
|
23
|
+
const scale = this.parameters.scale;
|
|
24
|
+
const uv = new Float32Array(count * 2);
|
|
25
|
+
for (let i = 0; i < count; i++) {
|
|
26
|
+
const x = posAttr.getX(i);
|
|
27
|
+
const y = posAttr.getY(i);
|
|
28
|
+
const z = posAttr.getZ(i);
|
|
29
|
+
// Use normal if available, else use position as approximation
|
|
30
|
+
const nx = nrmAttr ? nrmAttr.getX(i) : x;
|
|
31
|
+
const ny = nrmAttr ? nrmAttr.getY(i) : y;
|
|
32
|
+
const nz = nrmAttr ? nrmAttr.getZ(i) : z;
|
|
33
|
+
const ax = Math.abs(nx), ay = Math.abs(ny), az = Math.abs(nz);
|
|
34
|
+
let u, v;
|
|
35
|
+
if (ax >= ay && ax >= az) {
|
|
36
|
+
// X-dominant face
|
|
37
|
+
u = nx > 0 ? -z : z;
|
|
38
|
+
v = y;
|
|
39
|
+
}
|
|
40
|
+
else if (ay >= ax && ay >= az) {
|
|
41
|
+
// Y-dominant face
|
|
42
|
+
u = x;
|
|
43
|
+
v = ny > 0 ? z : -z;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Z-dominant face
|
|
47
|
+
u = nz > 0 ? x : -x;
|
|
48
|
+
v = y;
|
|
49
|
+
}
|
|
50
|
+
uv[i * 2] = (u / scale) * 0.5 + 0.5;
|
|
51
|
+
uv[i * 2 + 1] = (v / scale) * 0.5 + 0.5;
|
|
52
|
+
}
|
|
53
|
+
result.setAttribute('uv', new BufferAttribute(uv, 2));
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=CubeProjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CubeProjection.js","sourceRoot":"","sources":["../../src/unwrappers/CubeProjection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BufferGeometry } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
export interface CylinderProjectionOptions {
|
|
4
|
+
/** UV scale along the circumference. Default 1.0. */
|
|
5
|
+
scaleU?: number;
|
|
6
|
+
/** UV scale along the cylinder axis. Default 1.0. */
|
|
7
|
+
scaleV?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* CylinderProjection — Blender UV: Cylinder Projection
|
|
11
|
+
*
|
|
12
|
+
* Wraps UVs around the Y-axis.
|
|
13
|
+
* U = angle around Y-axis mapped to [0,1].
|
|
14
|
+
* V = normalised height along Y-axis.
|
|
15
|
+
*/
|
|
16
|
+
export declare class CylinderProjection extends BaseUnwrapper {
|
|
17
|
+
readonly unwrapType = "CylinderProjection";
|
|
18
|
+
parameters: {
|
|
19
|
+
scaleU: number;
|
|
20
|
+
scaleV: number;
|
|
21
|
+
};
|
|
22
|
+
constructor(opts?: CylinderProjectionOptions);
|
|
23
|
+
apply(geometry: BufferGeometry): BufferGeometry;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=CylinderProjection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CylinderProjection.d.ts","sourceRoot":"","sources":["../../src/unwrappers/CylinderProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAExD,MAAM,WAAW,yBAAyB;IACxC,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;GAMG;AACH,qBAAa,kBAAmB,SAAQ,aAAa;IACnD,QAAQ,CAAC,UAAU,wBAAuB;IAC1C,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,yBAA8B;IAQhD,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc;CAkChD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { BufferAttribute } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
/**
|
|
4
|
+
* CylinderProjection — Blender UV: Cylinder Projection
|
|
5
|
+
*
|
|
6
|
+
* Wraps UVs around the Y-axis.
|
|
7
|
+
* U = angle around Y-axis mapped to [0,1].
|
|
8
|
+
* V = normalised height along Y-axis.
|
|
9
|
+
*/
|
|
10
|
+
export class CylinderProjection extends BaseUnwrapper {
|
|
11
|
+
constructor(opts = {}) {
|
|
12
|
+
super();
|
|
13
|
+
this.unwrapType = 'CylinderProjection';
|
|
14
|
+
this.parameters = {
|
|
15
|
+
scaleU: opts.scaleU ?? 1.0,
|
|
16
|
+
scaleV: opts.scaleV ?? 1.0,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
apply(geometry) {
|
|
20
|
+
if (!this.enabled)
|
|
21
|
+
return geometry.clone();
|
|
22
|
+
const result = geometry.clone();
|
|
23
|
+
const posAttr = result.getAttribute('position');
|
|
24
|
+
const count = posAttr.count;
|
|
25
|
+
// Find Y extents
|
|
26
|
+
let yMin = Infinity, yMax = -Infinity;
|
|
27
|
+
for (let i = 0; i < count; i++) {
|
|
28
|
+
const y = posAttr.getY(i);
|
|
29
|
+
if (y < yMin)
|
|
30
|
+
yMin = y;
|
|
31
|
+
if (y > yMax)
|
|
32
|
+
yMax = y;
|
|
33
|
+
}
|
|
34
|
+
const yRange = yMax - yMin || 1;
|
|
35
|
+
const { scaleU, scaleV } = this.parameters;
|
|
36
|
+
const uv = new Float32Array(count * 2);
|
|
37
|
+
for (let i = 0; i < count; i++) {
|
|
38
|
+
const x = posAttr.getX(i);
|
|
39
|
+
const y = posAttr.getY(i);
|
|
40
|
+
const z = posAttr.getZ(i);
|
|
41
|
+
const u = (Math.atan2(x, z) / (2 * Math.PI) + 0.5) / scaleU;
|
|
42
|
+
const v = ((y - yMin) / yRange) / scaleV;
|
|
43
|
+
uv[i * 2] = u;
|
|
44
|
+
uv[i * 2 + 1] = v;
|
|
45
|
+
}
|
|
46
|
+
result.setAttribute('uv', new BufferAttribute(uv, 2));
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=CylinderProjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CylinderProjection.js","sourceRoot":"","sources":["../../src/unwrappers/CylinderProjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAE,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AASxD;;;;;;GAMG;AACH,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IAInD,YAAY,OAAkC,EAAE;QAC9C,KAAK,EAAE,CAAA;QAJA,eAAU,GAAG,oBAAoB,CAAA;QAKxC,IAAI,CAAC,UAAU,GAAG;YAChB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,GAAG;YAC1B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,GAAG;SAC3B,CAAA;IACH,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;QAE7B,iBAAiB;QACjB,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAA;QACrC,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,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAA;YACtB,IAAI,CAAC,GAAG,IAAI;gBAAE,IAAI,GAAG,CAAC,CAAA;QACxB,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,CAAA;QAE/B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAA;QAC1C,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QAEtC,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;YAEzB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAA;YAC3D,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,GAAsB,MAAM,CAAA;YAE3D,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,30 @@
|
|
|
1
|
+
import { BufferGeometry } from 'three';
|
|
2
|
+
import { BaseUnwrapper } from '../core/BaseUnwrapper.js';
|
|
3
|
+
export interface SmartUVProjectOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Maximum angle (degrees) between face normals for them to be grouped
|
|
6
|
+
* into the same projection island. Default 66. Blender: Smart UV Project → Angle Limit.
|
|
7
|
+
*/
|
|
8
|
+
angleLimit?: number;
|
|
9
|
+
/** Margin between islands in UV space [0–0.1]. Default 0.02. Blender: Island Margin */
|
|
10
|
+
islandMargin?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* SmartUVProject — Blender UV: Smart UV Project
|
|
14
|
+
*
|
|
15
|
+
* Clusters faces by normal direction (within angleLimit degrees),
|
|
16
|
+
* projects each cluster onto its average normal plane,
|
|
17
|
+
* then packs all islands into UV [0,1]² space.
|
|
18
|
+
*
|
|
19
|
+
* Good for hard-surface models with mixed orientations.
|
|
20
|
+
*/
|
|
21
|
+
export declare class SmartUVProject extends BaseUnwrapper {
|
|
22
|
+
readonly unwrapType = "SmartUVProject";
|
|
23
|
+
parameters: {
|
|
24
|
+
angleLimit: number;
|
|
25
|
+
islandMargin: number;
|
|
26
|
+
};
|
|
27
|
+
constructor(opts?: SmartUVProjectOptions);
|
|
28
|
+
apply(geometry: BufferGeometry): BufferGeometry;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=SmartUVProject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartUVProject.d.ts","sourceRoot":"","sources":["../../src/unwrappers/SmartUVProject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAExD,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAe,SAAQ,aAAa;IAC/C,QAAQ,CAAC,UAAU,oBAAmB;IACtC,UAAU,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;gBAE5C,IAAI,GAAE,qBAA0B;IAQ5C,KAAK,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc;CA2MhD"}
|