@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.
Files changed (53) hide show
  1. package/dist/core/BaseUnwrapper.d.ts +24 -0
  2. package/dist/core/BaseUnwrapper.d.ts.map +1 -0
  3. package/dist/core/BaseUnwrapper.js +16 -0
  4. package/dist/core/BaseUnwrapper.js.map +1 -0
  5. package/dist/index.d.ts +19 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +15 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/operations/AverageIslandScale.d.ts +15 -0
  10. package/dist/operations/AverageIslandScale.d.ts.map +1 -0
  11. package/dist/operations/AverageIslandScale.js +109 -0
  12. package/dist/operations/AverageIslandScale.js.map +1 -0
  13. package/dist/operations/MarkSeams.d.ts +34 -0
  14. package/dist/operations/MarkSeams.d.ts.map +1 -0
  15. package/dist/operations/MarkSeams.js +41 -0
  16. package/dist/operations/MarkSeams.js.map +1 -0
  17. package/dist/operations/PackIslands.d.ts +22 -0
  18. package/dist/operations/PackIslands.d.ts.map +1 -0
  19. package/dist/operations/PackIslands.js +129 -0
  20. package/dist/operations/PackIslands.js.map +1 -0
  21. package/dist/unwrappers/AngleBasedABF.d.ts +30 -0
  22. package/dist/unwrappers/AngleBasedABF.d.ts.map +1 -0
  23. package/dist/unwrappers/AngleBasedABF.js +162 -0
  24. package/dist/unwrappers/AngleBasedABF.js.map +1 -0
  25. package/dist/unwrappers/ConformalLSCM.d.ts +29 -0
  26. package/dist/unwrappers/ConformalLSCM.d.ts.map +1 -0
  27. package/dist/unwrappers/ConformalLSCM.js +165 -0
  28. package/dist/unwrappers/ConformalLSCM.js.map +1 -0
  29. package/dist/unwrappers/CubeProjection.d.ts +22 -0
  30. package/dist/unwrappers/CubeProjection.d.ts.map +1 -0
  31. package/dist/unwrappers/CubeProjection.js +57 -0
  32. package/dist/unwrappers/CubeProjection.js.map +1 -0
  33. package/dist/unwrappers/CylinderProjection.d.ts +25 -0
  34. package/dist/unwrappers/CylinderProjection.d.ts.map +1 -0
  35. package/dist/unwrappers/CylinderProjection.js +50 -0
  36. package/dist/unwrappers/CylinderProjection.js.map +1 -0
  37. package/dist/unwrappers/SmartUVProject.d.ts +30 -0
  38. package/dist/unwrappers/SmartUVProject.d.ts.map +1 -0
  39. package/dist/unwrappers/SmartUVProject.js +212 -0
  40. package/dist/unwrappers/SmartUVProject.js.map +1 -0
  41. package/dist/unwrappers/SphereProjection.d.ts +24 -0
  42. package/dist/unwrappers/SphereProjection.d.ts.map +1 -0
  43. package/dist/unwrappers/SphereProjection.js +40 -0
  44. package/dist/unwrappers/SphereProjection.js.map +1 -0
  45. package/dist/utils/linearSolver.d.ts +19 -0
  46. package/dist/utils/linearSolver.d.ts.map +1 -0
  47. package/dist/utils/linearSolver.js +73 -0
  48. package/dist/utils/linearSolver.js.map +1 -0
  49. package/dist/utils/meshGraph.d.ts +71 -0
  50. package/dist/utils/meshGraph.d.ts.map +1 -0
  51. package/dist/utils/meshGraph.js +315 -0
  52. package/dist/utils/meshGraph.js.map +1 -0
  53. 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":"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"}
@@ -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"}