xl-public-utils 1.0.4 → 1.0.6
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/index.d.ts +677 -79
- package/index.js +2 -1
- package/package.json +12 -4
- package/src/drcUtils.mjs +353 -0
- package/src/exportPLY.mjs +285 -0
- package/src/exportSTL.mjs +222 -0
- package/src/qrcode.mjs +16 -16
- package/src/utils.mjs +43 -1
- package/src/vtkUtils.mjs +780 -1
- package/tsconfig.json +9 -0
- package/types/bwip-js.d.mts +106 -0
- package/types/bwipp.d.mts +5 -0
- package/types/drcUtils.d.mts +129 -0
- package/types/exportPLY.d.mts +16 -0
- package/types/exportSTL.d.mts +19 -0
- package/types/qrcode.d.mts +92 -0
- package/types/utils.d.mts +28 -0
- package/types/vtkUtils.d.mts +320 -0
package/index.js
CHANGED
|
@@ -2,4 +2,5 @@ import * as vtkUtils from './src/vtkUtils.mjs';
|
|
|
2
2
|
import * as utils from './src/utils.mjs'
|
|
3
3
|
import * as qrcode from './src/qrcode.mjs'
|
|
4
4
|
import * as BwipJs from './src/bwip-js.mjs';
|
|
5
|
-
|
|
5
|
+
import * as drcUtils from './src/drcUtils.mjs';
|
|
6
|
+
export { vtkUtils, utils, qrcode, BwipJs, drcUtils };
|
package/package.json
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xl-public-utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "index.js",
|
|
6
7
|
"module": "index.js",
|
|
7
8
|
"types": "index.d.ts",
|
|
8
9
|
"peerDependencies": {
|
|
9
|
-
"@kitware/vtk.js": ">=25.7.2"
|
|
10
|
+
"@kitware/vtk.js": ">=25.7.2",
|
|
11
|
+
"gl-matrix": ">=3.4.3"
|
|
10
12
|
},
|
|
11
13
|
"peerDependenciesMeta": {
|
|
12
14
|
"@kitware/vtk.js": {
|
|
13
15
|
"optional": true
|
|
16
|
+
},
|
|
17
|
+
"gl-matrix": {
|
|
18
|
+
"optional": true
|
|
14
19
|
}
|
|
15
20
|
},
|
|
16
21
|
"exports": {
|
|
@@ -21,11 +26,14 @@
|
|
|
21
26
|
"./bwipJs": "./src/bwip-js.mjs"
|
|
22
27
|
},
|
|
23
28
|
"scripts": {
|
|
24
|
-
"test": "
|
|
29
|
+
"test": "jest"
|
|
25
30
|
},
|
|
26
31
|
"keywords": [
|
|
27
32
|
"一些通用方法"
|
|
28
33
|
],
|
|
29
34
|
"author": "xl",
|
|
30
|
-
"license": "ISC"
|
|
35
|
+
"license": "ISC",
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typescript": "^5.6.3"
|
|
38
|
+
}
|
|
31
39
|
}
|
package/src/drcUtils.mjs
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { vec3 } from "gl-matrix";
|
|
2
|
+
import { int8ArrayToBase64 } from './utils.mjs'
|
|
3
|
+
import vtkPolyData from "@kitware/vtk.js/Common/DataModel/PolyData";
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} AttrOption
|
|
6
|
+
* @property {number[]} [colors] 颜色数组
|
|
7
|
+
* @property {number[]} [generics] 属性数组
|
|
8
|
+
* @property {number[]} [normals] 法向量数组
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 将数据进行压缩
|
|
13
|
+
* @param {number[]} vertices 网格点
|
|
14
|
+
* @param {number[]} faces 网格面,必须是三角化之后的面,每连续的三个点索引标识一个面
|
|
15
|
+
* @param {number} byteLength=14 byteLength 压缩率
|
|
16
|
+
* @param {AttrOption} [attr] 其他需要压缩的属性
|
|
17
|
+
* @returns {Int8Array} 压缩之后的DRC数据
|
|
18
|
+
*/
|
|
19
|
+
export function enCodeMeshByVF(vertices, faces, byteLength = 14, attr =undefined) {
|
|
20
|
+
const facesFlat = faces;
|
|
21
|
+
const pointFlat = vertices;
|
|
22
|
+
const encoderModule = window.encoderModule
|
|
23
|
+
const encoder = new encoderModule.Encoder();
|
|
24
|
+
const meshBuilder = new encoderModule.MeshBuilder();
|
|
25
|
+
const dracoMesh = new encoderModule.Mesh();
|
|
26
|
+
const numberFaces = faces.length / 3;
|
|
27
|
+
const numberPoints = vertices.length / 3;
|
|
28
|
+
meshBuilder.AddFacesToMesh(dracoMesh, numberFaces, facesFlat);
|
|
29
|
+
meshBuilder.AddFloatAttributeToMesh(dracoMesh, encoderModule.POSITION, numberPoints, 3, pointFlat);
|
|
30
|
+
if (attr) {
|
|
31
|
+
if (attr.colors && attr.colors.length) {
|
|
32
|
+
meshBuilder.AddFloatAttributeToMesh(dracoMesh, encoderModule.COLOR, numberPoints, 3, attr.colors);
|
|
33
|
+
}
|
|
34
|
+
if (attr.normals && attr.normals.length) {
|
|
35
|
+
meshBuilder.AddFloatAttributeToMesh(dracoMesh, encoderModule.NORMAL, numberPoints, 3, attr.normals);
|
|
36
|
+
}
|
|
37
|
+
if (attr.generics && attr.generics.length) {
|
|
38
|
+
meshBuilder.AddFloatAttributeToMesh(dracoMesh, encoderModule.GENERIC, numberPoints, 3, attr.generics);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// encoder.SetSpeedOptions(5, 5);
|
|
43
|
+
encoder.SetAttributeQuantization(encoderModule.POSITION, byteLength);
|
|
44
|
+
encoder.SetEncodingMethod(encoderModule.MESH_EDGEBREAKER_ENCODING);
|
|
45
|
+
const encodedData = new encoderModule.DracoInt8Array();
|
|
46
|
+
const encodedLen = encoder.EncodeMeshToDracoBuffer(dracoMesh, encodedData);
|
|
47
|
+
const outputBuffer = new ArrayBuffer(encodedLen);
|
|
48
|
+
const outputData = new Int8Array(outputBuffer);
|
|
49
|
+
for (let i = 0; i < encodedLen; ++i) {
|
|
50
|
+
outputData[i] = encodedData.GetValue(i);
|
|
51
|
+
}
|
|
52
|
+
encoderModule.destroy(dracoMesh);
|
|
53
|
+
encoderModule.destroy(encoder);
|
|
54
|
+
encoderModule.destroy(meshBuilder);
|
|
55
|
+
return outputData
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 使用drc压缩点
|
|
60
|
+
* @param {vec3[] | number[]} points 点数组
|
|
61
|
+
* @returns {Int8Array} 压缩之后的drc data
|
|
62
|
+
*/
|
|
63
|
+
export function enCodePointCloud(points) {
|
|
64
|
+
const encoderModule = window.encoderModule
|
|
65
|
+
const encoder = new encoderModule.Encoder();
|
|
66
|
+
const pointCloudBuilder = new encoderModule.PointCloudBuilder();
|
|
67
|
+
const dracoPointCloud = new encoderModule.PointCloud();
|
|
68
|
+
const pointsFlat = [];
|
|
69
|
+
for(let i= 0; i <points.length; i++) {
|
|
70
|
+
if(typeof points[i] === 'object') {
|
|
71
|
+
pointsFlat.push(points[i][0], points[i][1], points[i][2]);
|
|
72
|
+
} else {
|
|
73
|
+
pointsFlat.push(points[i])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const numberPoints = pointsFlat.length / 3;
|
|
77
|
+
pointCloudBuilder.AddFloatAttribute(dracoPointCloud, encoderModule.POSITION, numberPoints, 3, pointsFlat);
|
|
78
|
+
|
|
79
|
+
encoder.SetAttributeQuantization(encoderModule.POSITION, 14);
|
|
80
|
+
|
|
81
|
+
encoder.SetEncodingMethod(encoderModule.MESH_SEQUENTIAL_ENCODING);
|
|
82
|
+
const encodedData = new encoderModule.DracoInt8Array();
|
|
83
|
+
const encodedLen = encoder.EncodePointCloudToDracoBuffer(dracoPointCloud, false, encodedData);
|
|
84
|
+
const outputBuffer = new ArrayBuffer(encodedLen);
|
|
85
|
+
const outputData = new Int8Array(outputBuffer);
|
|
86
|
+
for (let i = 0; i < encodedLen; ++i) {
|
|
87
|
+
outputData[i] = encodedData.GetValue(i);
|
|
88
|
+
}
|
|
89
|
+
encoderModule.destroy(dracoPointCloud);
|
|
90
|
+
encoderModule.destroy(encoder);
|
|
91
|
+
encoderModule.destroy(pointCloudBuilder);
|
|
92
|
+
return outputData
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 解压drc格式的点云数据
|
|
96
|
+
* @param {ArrayBuffer} byteArray drc点数据
|
|
97
|
+
* @returns {Float32Array} 点数组
|
|
98
|
+
*/
|
|
99
|
+
export function decodePointCloud(byteArray) {
|
|
100
|
+
const dracoPointCloud = decodeBuffer(byteArray);
|
|
101
|
+
return getPointCloudFromDracoGeometry(dracoPointCloud);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @typedef {Object} DrcMeshData
|
|
106
|
+
* @property {Uint32Array} faces 网格面数组
|
|
107
|
+
* @property {Float32Array} vertices 网格点数组
|
|
108
|
+
* @property {Float32Array} [colors] 颜色数组
|
|
109
|
+
* @property {Float32Array} [generics] 属性数组
|
|
110
|
+
* @property {Float32Array} [normals] 法向量数组
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 解压drc格式的网格数据
|
|
115
|
+
* @param {ArrayBuffer} byteArray drc网格数据
|
|
116
|
+
* @returns {DrcMeshData} 网格数据
|
|
117
|
+
*/
|
|
118
|
+
export function decodeDrcBufferArray(byteArray) {
|
|
119
|
+
var dracoGeometry = decodeBuffer(byteArray);
|
|
120
|
+
return getPolyDataFromDracoGeometry(dracoGeometry);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @typedef {Object} Decoder
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 输入一个Drc数据,反会对应的解压器
|
|
128
|
+
* @param {ArrayBuffer} arrayBuffer 输入的数据
|
|
129
|
+
* @returns {Decoder} drc解压类
|
|
130
|
+
*/
|
|
131
|
+
export function decodeBuffer(arrayBuffer) {
|
|
132
|
+
const decoderModule = window.decoderModule;
|
|
133
|
+
var byteArray = new Int8Array(arrayBuffer);
|
|
134
|
+
var decoder = new decoderModule.Decoder();
|
|
135
|
+
var decoderBuffer = new decoderModule.DecoderBuffer();
|
|
136
|
+
decoderBuffer.Init(byteArray, byteArray.length);
|
|
137
|
+
var geometryType = decoder.GetEncodedGeometryType(decoderBuffer);
|
|
138
|
+
var dracoGeometry;
|
|
139
|
+
|
|
140
|
+
if (geometryType === decoderModule.TRIANGULAR_MESH) {
|
|
141
|
+
dracoGeometry = new decoderModule.Mesh();
|
|
142
|
+
var status = decoder.DecodeBufferToMesh(decoderBuffer, dracoGeometry);
|
|
143
|
+
|
|
144
|
+
if (!status.ok()) {
|
|
145
|
+
console.error("Could not decode Draco file: ".concat(status.error_msg()));
|
|
146
|
+
}
|
|
147
|
+
} else if ( geometryType === decoderModule.POINT_CLOUD ) {
|
|
148
|
+
dracoGeometry = new decoderModule.PointCloud();
|
|
149
|
+
var status =
|
|
150
|
+
decoder.DecodeBufferToPointCloud(decoderBuffer, dracoGeometry);
|
|
151
|
+
if(!status.ok()) {
|
|
152
|
+
console.error('Wrong geometry type, expected mesh, got point cloud.');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
decoderModule.destroy(decoderBuffer);
|
|
157
|
+
decoderModule.destroy(decoder);
|
|
158
|
+
return dracoGeometry;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 获取drcDecoder中的网格信息
|
|
163
|
+
* @param {Decoder} dracoGeometry DRC网格的decoder
|
|
164
|
+
* @returns {DrcMeshData} decoder中的网格信息
|
|
165
|
+
*/
|
|
166
|
+
function getPolyDataFromDracoGeometry(dracoGeometry) {
|
|
167
|
+
const decoderModule = window.decoderModule;
|
|
168
|
+
var decoder = new decoderModule.Decoder(); // Get position attribute ID
|
|
169
|
+
|
|
170
|
+
var positionAttributeId = decoder.GetAttributeId(dracoGeometry, decoderModule.POSITION);
|
|
171
|
+
|
|
172
|
+
if (positionAttributeId === -1) {
|
|
173
|
+
console.error('No position attribute found in the decoded model.');
|
|
174
|
+
decoderModule.destroy(decoder);
|
|
175
|
+
decoderModule.destroy(dracoGeometry);
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
var positionArray = getDracoAttributeAsFloat32Array(dracoGeometry, positionAttributeId); // Read indices
|
|
179
|
+
var i = dracoGeometry.num_faces();
|
|
180
|
+
var indices = new Uint32Array(i * 4);
|
|
181
|
+
var indicesArray = new decoderModule.DracoInt32Array();
|
|
182
|
+
while (i--) {
|
|
183
|
+
decoder.GetFaceFromMesh(dracoGeometry, i, indicesArray);
|
|
184
|
+
var index = i * 4;
|
|
185
|
+
indices[index] = 3;
|
|
186
|
+
indices[index + 1] = indicesArray.GetValue(0);
|
|
187
|
+
indices[index + 2] = indicesArray.GetValue(1);
|
|
188
|
+
indices[index + 3] = indicesArray.GetValue(2);
|
|
189
|
+
} // Create polyData and add positions and indinces
|
|
190
|
+
var normalAttributeId = decoder.GetAttributeId(dracoGeometry, decoderModule.NORMAL);
|
|
191
|
+
var normals;
|
|
192
|
+
if (normalAttributeId !== -1) {
|
|
193
|
+
normals = getDracoAttributeAsFloat32Array(dracoGeometry, decoderModule.NORMAL);
|
|
194
|
+
} // Texture coordinates
|
|
195
|
+
var genericattributeId = decoder.GetAttributeId(dracoGeometry, decoderModule.GENERIC);
|
|
196
|
+
let generics;
|
|
197
|
+
if (genericattributeId !== -1) {
|
|
198
|
+
generics = getDracoAttributeAsFloat32Array(dracoGeometry, genericattributeId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// var texCoordAttributeId = decoder.GetAttributeId(dracoGeometry, decoderModule.TEX_COORD);
|
|
202
|
+
|
|
203
|
+
// if (texCoordAttributeId !== -1) {
|
|
204
|
+
// var texCoordArray = getDracoAttributeAsFloat32Array(dracoGeometry, texCoordAttributeId);
|
|
205
|
+
// var texCoords = vtkDataArray.newInstance({
|
|
206
|
+
// numberOfComponents: 2,
|
|
207
|
+
// values: texCoordArray,
|
|
208
|
+
// name: 'TCoords'
|
|
209
|
+
// });
|
|
210
|
+
// } // Scalars
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
var colorAttributeId = decoder.GetAttributeId(dracoGeometry, decoderModule.COLOR);
|
|
214
|
+
let colors;
|
|
215
|
+
if (colorAttributeId !== -1) {
|
|
216
|
+
colors = getDracoAttributeAsFloat32Array(dracoGeometry, colorAttributeId);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
decoderModule.destroy(decoder);
|
|
220
|
+
return {
|
|
221
|
+
faces: indices,
|
|
222
|
+
vertices: positionArray,
|
|
223
|
+
normals,
|
|
224
|
+
colors,
|
|
225
|
+
generics
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* 解压DRC点云数据
|
|
231
|
+
* @param {Decoder} dracoGeometry DRC点云的Decder
|
|
232
|
+
* @returns {Float32Array} 点
|
|
233
|
+
*/
|
|
234
|
+
function getPointCloudFromDracoGeometry(dracoGeometry) {
|
|
235
|
+
const decoderModule = window.decoderModule;
|
|
236
|
+
var decoder = new decoderModule.Decoder(); // Get position attribute ID
|
|
237
|
+
|
|
238
|
+
var positionAttributeId = decoder.GetAttributeId(dracoGeometry, decoderModule.POSITION);
|
|
239
|
+
|
|
240
|
+
if (positionAttributeId === -1) {
|
|
241
|
+
console.error('No position attribute found in the decoded model.');
|
|
242
|
+
decoderModule.destroy(decoder);
|
|
243
|
+
decoderModule.destroy(dracoGeometry);
|
|
244
|
+
return new Float32Array();
|
|
245
|
+
} else {
|
|
246
|
+
const points = getDracoAttributeAsFloat32Array(dracoGeometry, positionAttributeId); // Read indices
|
|
247
|
+
decoderModule.destroy(decoder);
|
|
248
|
+
return points;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 获取decoder中的属性
|
|
253
|
+
* @param {Decoder} dracoGeometry DRC Decoder
|
|
254
|
+
* @param {number} attributeId 需要获取的属性id
|
|
255
|
+
* @returns {Float32Array} 对应decoder中的属性
|
|
256
|
+
*/
|
|
257
|
+
export function getDracoAttributeAsFloat32Array(dracoGeometry, attributeId) {
|
|
258
|
+
const decoderModule = window.decoderModule;
|
|
259
|
+
var decoder = new decoderModule.Decoder();
|
|
260
|
+
var attribute = decoder.GetAttribute(dracoGeometry, attributeId);
|
|
261
|
+
var numberOfComponents = attribute.num_components();
|
|
262
|
+
var numberOfPoints = dracoGeometry.num_points();
|
|
263
|
+
var attributeData = new decoderModule.DracoFloat32Array();
|
|
264
|
+
decoder.GetAttributeFloatForAllPoints(dracoGeometry, attribute, attributeData);
|
|
265
|
+
var i = numberOfPoints * numberOfComponents;
|
|
266
|
+
var attributeArray = new Float32Array(i);
|
|
267
|
+
while (i--) {
|
|
268
|
+
attributeArray[i] = attributeData.GetValue(i);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return attributeArray;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 将点数据使用drc压缩处理为 base64 string
|
|
277
|
+
* @param {vec3[] | number[]} points 点数据
|
|
278
|
+
* @returns {string} drc压缩之后的base64数据
|
|
279
|
+
*/
|
|
280
|
+
|
|
281
|
+
export function enCloudPointTobase64(points) {
|
|
282
|
+
return int8ArrayToBase64(enCodePointCloud(points));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* @typedef {Object} Mesh
|
|
288
|
+
* @property {number[][]} vertices 点信息
|
|
289
|
+
* @property {number[][]} faces 面信息
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* 使用drc压缩网格信息
|
|
294
|
+
* @param {Mesh | vtkPolyData} mesh
|
|
295
|
+
* @param {number} byteLength=14 压缩率
|
|
296
|
+
* @param {AttrOption} [attr] 其他需要压缩的属性
|
|
297
|
+
* @returns {Int8Array} 压缩之后的DRC数据
|
|
298
|
+
*/
|
|
299
|
+
|
|
300
|
+
export function enCodeMesh(mesh, byteLength = 14, attr = undefined) {
|
|
301
|
+
let faces = [];
|
|
302
|
+
let vertices = [];
|
|
303
|
+
if(mesh.vertices && mesh.faces) {
|
|
304
|
+
vertices = mesh.vertices;
|
|
305
|
+
for(let i = 0; i < mesh.faces.length; i++) {
|
|
306
|
+
const face = mesh.faces[i];
|
|
307
|
+
faces.push(face[0]);
|
|
308
|
+
faces.push(face[1]);
|
|
309
|
+
faces.push(face[2]);
|
|
310
|
+
}
|
|
311
|
+
} else if(mesh.getClassName && mesh.getClassName() === 'vtkPolyData') {
|
|
312
|
+
vertices = mesh.getPoints().getData();
|
|
313
|
+
const _vtkFaces = mesh.getPolys().getData();
|
|
314
|
+
const nbFaces = _vtkFaces.length / 4;
|
|
315
|
+
for(let i = 0; i < nbFaces; i++) {
|
|
316
|
+
faces.push(_vtkFaces[i * 4 + 1]);
|
|
317
|
+
faces.push(_vtkFaces[i * 4 + 2]);
|
|
318
|
+
faces.push(_vtkFaces[i * 4 + 3]);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return enCodeMeshByVF(vertices, faces, byteLength, attr)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* 初始化Draco文件
|
|
326
|
+
*/
|
|
327
|
+
export function initDrcCoder() {
|
|
328
|
+
if (!window.encoderModule) {
|
|
329
|
+
const timerEncoder = setInterval(() => {
|
|
330
|
+
if (window.DracoEncoderModule) {
|
|
331
|
+
window.DracoEncoderModule().then((res) => {
|
|
332
|
+
window.encoderModule = res;
|
|
333
|
+
});
|
|
334
|
+
clearInterval(timerEncoder);
|
|
335
|
+
}
|
|
336
|
+
}, 50);
|
|
337
|
+
}
|
|
338
|
+
if (!window.decoderModule) {
|
|
339
|
+
const timerDecoder = setInterval(() => {
|
|
340
|
+
if (window.DracoDecoderModule) {
|
|
341
|
+
window.DracoDecoderModule().then((res) => {
|
|
342
|
+
window.decoderModule = res;
|
|
343
|
+
function a() {
|
|
344
|
+
return res;
|
|
345
|
+
}
|
|
346
|
+
// show.value = true;
|
|
347
|
+
vtkDracoReader.setDracoDecoder(a);
|
|
348
|
+
});
|
|
349
|
+
clearInterval(timerDecoder);
|
|
350
|
+
}
|
|
351
|
+
}, 50);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import vtkPolyData from "@kitware/vtk.js/Common/DataModel/PolyData";
|
|
2
|
+
import { mat4 } from "gl-matrix";
|
|
3
|
+
var FormatTypes = {
|
|
4
|
+
ASCII: 'ascii',
|
|
5
|
+
BINARY: 'binary'
|
|
6
|
+
};
|
|
7
|
+
var TextureCoordinatesName = {
|
|
8
|
+
UV: ['u', 'v'],
|
|
9
|
+
TEXTURE_UV: ['texture_u', 'texture_v']
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
var DEFAULT_VALUES = {
|
|
13
|
+
format: FormatTypes.ASCII,
|
|
14
|
+
dataByteOrder: 0,
|
|
15
|
+
headerComments: [],
|
|
16
|
+
textureFileName: '',
|
|
17
|
+
textureCoordinatesName: TextureCoordinatesName.UV,
|
|
18
|
+
withNormals: false,
|
|
19
|
+
withUVs: false,
|
|
20
|
+
withColors: false,
|
|
21
|
+
withIndices: true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var _writeHeader = function writeHeader(polyData, fileFormat, fileType, headerComments, textureFileName, textureCoordinatesName, vertexCount, faceListLength, withNormals, withUVs, withColors, withIndices) {
|
|
25
|
+
var isBinary = fileFormat !== FormatTypes.ASCII;
|
|
26
|
+
var format;
|
|
27
|
+
|
|
28
|
+
if (isBinary) {
|
|
29
|
+
format = fileType ? 'binary_little_endian' : 'binary_big_endian';
|
|
30
|
+
} else format = 'ascii';
|
|
31
|
+
|
|
32
|
+
headerComments.unshift('VTK.js generated PLY File');
|
|
33
|
+
|
|
34
|
+
if (textureFileName) {
|
|
35
|
+
headerComments.push("TextureFile ".concat(textureFileName));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var commentElements = headerComments.map(function (comment) {
|
|
39
|
+
return "comment ".concat(comment);
|
|
40
|
+
}).join('\n');
|
|
41
|
+
var header = ['ply', "format ".concat(format, " 1.0"), "".concat(commentElements), "element vertex ".concat(vertexCount), 'property float x', 'property float y', 'property float z']; // normals
|
|
42
|
+
|
|
43
|
+
if (withNormals) {
|
|
44
|
+
header.push('property float nx', 'property float ny', 'property float nz');
|
|
45
|
+
} // uvs
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if (withUVs) {
|
|
49
|
+
header.push("property float ".concat(textureCoordinatesName[0]), "property float ".concat(textureCoordinatesName[1]));
|
|
50
|
+
} // colors
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if (withColors) {
|
|
54
|
+
header.push('property uchar red', 'property uchar green', 'property uchar blue');
|
|
55
|
+
} // faces
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if (withIndices) {
|
|
59
|
+
header.push("element face ".concat(faceListLength), 'property list uchar int vertex_indices');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
header.push('end_header\n');
|
|
63
|
+
return header.join('\n');
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
var binaryWriter = function binaryWriter() {
|
|
67
|
+
var output;
|
|
68
|
+
var vOffset;
|
|
69
|
+
var fOffset;
|
|
70
|
+
var indexByteCount = 4;
|
|
71
|
+
var ft;
|
|
72
|
+
return {
|
|
73
|
+
init: function init(polyData) {},
|
|
74
|
+
writeHeader: function writeHeader(polyData, fileFormat, fileType, headerComments, textureFileName, textureCoordinatesName, numPts, numPolys, withNormals, withUVs, withColors, withIndices) {
|
|
75
|
+
var vertexCount = polyData.getPoints().getNumberOfPoints();
|
|
76
|
+
ft = fileType; // 1 byte shape descriptor
|
|
77
|
+
// 3 vertex indices at ${indexByteCount} bytes
|
|
78
|
+
|
|
79
|
+
var faceListLength = withIndices ? numPolys * (indexByteCount * 3 + 1) : 0; // 3 position values at 4 bytes
|
|
80
|
+
// 3 normal values at 4 bytes
|
|
81
|
+
// 3 color channels with 1 byte
|
|
82
|
+
// 2 uv values at 4 bytes
|
|
83
|
+
|
|
84
|
+
var vertexListLength = vertexCount * (4 * 3 + (withNormals ? 4 * 3 : 0) + (withUVs ? 4 * 2 : 0) + (withColors ? 3 : 0));
|
|
85
|
+
|
|
86
|
+
var header = _writeHeader(polyData, fileFormat, fileType, headerComments, textureFileName, textureCoordinatesName, numPts, numPolys, withNormals, withUVs, withColors, withIndices);
|
|
87
|
+
|
|
88
|
+
var headerBin = new TextEncoder().encode(header);
|
|
89
|
+
output = new DataView(new ArrayBuffer(headerBin.length + vertexListLength + faceListLength));
|
|
90
|
+
new Uint8Array(output.buffer).set(headerBin, 0);
|
|
91
|
+
vOffset = headerBin.length;
|
|
92
|
+
fOffset = vOffset + vertexListLength;
|
|
93
|
+
},
|
|
94
|
+
writeVertice: function writeVertice(x, y, z, nx, ny, nz, u, v, r, g, b) {
|
|
95
|
+
// xyz
|
|
96
|
+
output.setFloat32(vOffset, x, ft);
|
|
97
|
+
vOffset += 4;
|
|
98
|
+
output.setFloat32(vOffset, y, ft);
|
|
99
|
+
vOffset += 4;
|
|
100
|
+
output.setFloat32(vOffset, z, ft);
|
|
101
|
+
vOffset += 4; // nxnynz
|
|
102
|
+
|
|
103
|
+
if (nx !== null && ny !== null && nz !== null) {
|
|
104
|
+
output.setFloat32(vOffset, nx, ft);
|
|
105
|
+
vOffset += 4;
|
|
106
|
+
output.setFloat32(vOffset, ny, ft);
|
|
107
|
+
vOffset += 4;
|
|
108
|
+
output.setFloat32(vOffset, nz, ft);
|
|
109
|
+
vOffset += 4;
|
|
110
|
+
} // uv
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if (u !== null && v !== null) {
|
|
114
|
+
output.setFloat32(vOffset, u, ft);
|
|
115
|
+
vOffset += 4;
|
|
116
|
+
output.setFloat32(vOffset, v, ft);
|
|
117
|
+
vOffset += 4;
|
|
118
|
+
} // rgb
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if (r !== null && g !== null && b !== null) {
|
|
122
|
+
output.setUint8(vOffset, r);
|
|
123
|
+
vOffset += 1;
|
|
124
|
+
output.setUint8(vOffset, g);
|
|
125
|
+
vOffset += 1;
|
|
126
|
+
output.setUint8(vOffset, b);
|
|
127
|
+
vOffset += 1;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
writeFace: function writeFace(n, x, y, z) {
|
|
131
|
+
output.setUint8(fOffset, n);
|
|
132
|
+
fOffset += 1;
|
|
133
|
+
output.setUint32(fOffset, x, ft);
|
|
134
|
+
fOffset += indexByteCount;
|
|
135
|
+
output.setUint32(fOffset, y, ft);
|
|
136
|
+
fOffset += indexByteCount;
|
|
137
|
+
output.setUint32(fOffset, z, ft);
|
|
138
|
+
fOffset += indexByteCount;
|
|
139
|
+
},
|
|
140
|
+
writeFooter: function writeFooter(polyData) {},
|
|
141
|
+
getOutputData: function getOutputData() {
|
|
142
|
+
return output;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
var asciiWriter = function asciiWriter() {
|
|
148
|
+
var fileContent = '';
|
|
149
|
+
return {
|
|
150
|
+
init: function init(polyData) {},
|
|
151
|
+
writeHeader: function writeHeader(polyData, fileFormat, fileType, headerComments, textureFileName, textureCoordinatesName, numPts, numPolys, withNormals, withUVs, withColors, withIndices) {
|
|
152
|
+
fileContent += _writeHeader(polyData, fileFormat, fileType, headerComments, textureFileName, textureCoordinatesName, numPts, numPolys, withNormals, withUVs, withColors, withIndices);
|
|
153
|
+
},
|
|
154
|
+
writeVertice: function writeVertice(x, y, z, nx, ny, nz, u, v, r, g, b) {
|
|
155
|
+
fileContent += "".concat(x, " ").concat(y, " ").concat(z);
|
|
156
|
+
|
|
157
|
+
if (nx !== null && ny !== null && nz !== null) {
|
|
158
|
+
fileContent += " ".concat(nx, " ").concat(ny, " ").concat(nz);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (u !== null && v !== null) {
|
|
162
|
+
fileContent += " ".concat(u, " ").concat(v);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (r !== null && g !== null && b !== null) {
|
|
166
|
+
fileContent += " ".concat(r, " ").concat(g, " ").concat(b);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fileContent += '\n';
|
|
170
|
+
},
|
|
171
|
+
writeFace: function writeFace(n, x, y, z) {
|
|
172
|
+
fileContent += "".concat(n, " ").concat(x, " ").concat(y, " ").concat(z, "\n");
|
|
173
|
+
},
|
|
174
|
+
writeFooter: function writeFooter(polyData) {},
|
|
175
|
+
getOutputData: function getOutputData() {
|
|
176
|
+
return fileContent;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 将polyData转换为ply文件
|
|
183
|
+
* @param {vtkPolyData} polyData 需要导出的polydata
|
|
184
|
+
* @param {'ascii' | 'binary'} format 导出文件格式
|
|
185
|
+
* @param {0 | 1} dataByteOrder=0 字节序,默认为0, 0为大端模式, 1小端模式
|
|
186
|
+
* @param {string[]} headerComments=[] 文件头描述
|
|
187
|
+
* @param {string} textureFileName='' header描述文件名
|
|
188
|
+
* @param {['u', 'v']} textureCoordinatesName=['u', 'v'] header添加u和v
|
|
189
|
+
* @param {boolean} withNormals=false 是否需要法向量
|
|
190
|
+
* @param {boolean} withUVs=false 是否需要UV
|
|
191
|
+
* @param {boolean} withColors=false 是否需要Colors
|
|
192
|
+
* @param {boolean} withColors=true 面使用点索引
|
|
193
|
+
* @returns {string | DataView} 返回文件内容
|
|
194
|
+
*/
|
|
195
|
+
export function writePLY(polyData, format, dataByteOrder = DEFAULT_VALUES.dataByteOrder, headerComments = DEFAULT_VALUES.headerComments, textureFileName = DEFAULT_VALUES.textureFileName, textureCoordinatesName = DEFAULT_VALUES.textureCoordinatesName, withNormals = DEFAULT_VALUES.withNormals, withUVs = DEFAULT_VALUES.withUVs, withColors = DEFAULT_VALUES.withColors, withIndices = DEFAULT_VALUES.withIndices) {
|
|
196
|
+
var inPts = polyData.getPoints();
|
|
197
|
+
var polys = polyData.getPolys();
|
|
198
|
+
|
|
199
|
+
if (inPts === null || polys === null) {
|
|
200
|
+
console.error('输入错误!')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
var writer = null;
|
|
204
|
+
|
|
205
|
+
if (format === 'binary') {
|
|
206
|
+
writer = binaryWriter();
|
|
207
|
+
} else if (format === 'ascii') {
|
|
208
|
+
writer = asciiWriter();
|
|
209
|
+
} else {
|
|
210
|
+
console.error('Invalid type format');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
var tCoordsName = textureCoordinatesName;
|
|
214
|
+
|
|
215
|
+
if (typeof textureCoordinatesName === 'undefined') {
|
|
216
|
+
console.log('Invalid TextureCoordinatesName value, fallback to default uv values');
|
|
217
|
+
tCoordsName = TextureCoordinatesName.UV;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
writer.init(polyData);
|
|
221
|
+
var numPts = inPts.getNumberOfPoints();
|
|
222
|
+
var numPolys = polys.getNumberOfCells(); // textureCoords / uvs
|
|
223
|
+
|
|
224
|
+
var textureCoords = polyData.getPointData().getTCoords(); // eslint-disable-next-line no-param-reassign
|
|
225
|
+
|
|
226
|
+
// withUVs = !(textureCoords === null); // scalars / colors
|
|
227
|
+
|
|
228
|
+
var scalars = polyData.getPointData().getScalars(); // eslint-disable-next-line no-param-reassign
|
|
229
|
+
|
|
230
|
+
withColors = !(scalars === null);
|
|
231
|
+
var fileType = dataByteOrder ? 0 : 1;
|
|
232
|
+
writer.writeHeader(polyData, format, fileType, headerComments, textureFileName, tCoordsName, numPts, numPolys, withNormals, withUVs, withColors, withIndices);
|
|
233
|
+
var normals = polyData.getPointData().getNormals(); // points / vertices
|
|
234
|
+
|
|
235
|
+
for (var i = 0; i < numPts; i++) {
|
|
236
|
+
// eslint-disable-next-line prefer-const
|
|
237
|
+
var p = inPts.getPoint(i);
|
|
238
|
+
// divide by 1 to remove trailing zeros
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
var x = p[0].toFixed(8);
|
|
242
|
+
var y = p[1].toFixed(8);
|
|
243
|
+
var z = p[2].toFixed(8); // normals
|
|
244
|
+
|
|
245
|
+
var nx = null;
|
|
246
|
+
var ny = null;
|
|
247
|
+
var nz = null; // uvs
|
|
248
|
+
|
|
249
|
+
var u = null;
|
|
250
|
+
var v = null; // colors
|
|
251
|
+
|
|
252
|
+
var r = null;
|
|
253
|
+
var g = null;
|
|
254
|
+
var b = null;
|
|
255
|
+
|
|
256
|
+
// if (textureCoords) {
|
|
257
|
+
// u = textureCoords.getData()[i * 2];
|
|
258
|
+
// v = textureCoords.getData()[i * 2 + 1];
|
|
259
|
+
// }
|
|
260
|
+
|
|
261
|
+
if (scalars) {
|
|
262
|
+
r = scalars.getData()[i * 3];
|
|
263
|
+
g = scalars.getData()[i * 3 + 1];
|
|
264
|
+
b = scalars.getData()[i * 3 + 2];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (normals) {
|
|
268
|
+
nx = normals.getData()[i * 2];
|
|
269
|
+
ny = normals.getData()[i * 2 + 1];
|
|
270
|
+
nz = normals.getData()[i * 2 + 2];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
writer.writeVertice(x, y, z, nx, ny, nz, u, v, r, g, b);
|
|
274
|
+
} // polys / indices
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
var pd = polys.getData();
|
|
278
|
+
|
|
279
|
+
for (var _i = 0, l = pd.length; _i < l; _i += 4) {
|
|
280
|
+
writer.writeFace(pd[_i + 0], pd[_i + 1], pd[_i + 2], pd[_i + 3]);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
writer.writeFooter(polyData);
|
|
284
|
+
return writer.getOutputData();
|
|
285
|
+
}
|