Skip to content

Commit 05d7cf7

Browse files
dakerfinetjul
authored andcommitted
fix(PLYReader): add support for point clouds
fixes #2841
1 parent f9ef2ee commit 05d7cf7

File tree

4 files changed

+163
-14
lines changed

4 files changed

+163
-14
lines changed

Sources/IO/Geometry/PLYReader/example/index.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,8 @@ mapper.setInputConnection(reader.getOutputPort());
3939
// ----------------------------------------------------------------------------
4040

4141
function refresh() {
42-
const resetCamera = renderer.resetCamera;
43-
const render = renderWindow.render;
44-
resetCamera();
45-
render();
42+
renderer.resetCamera();
43+
renderWindow.render();
4644
}
4745

4846
function update() {
@@ -116,9 +114,7 @@ fileInput.addEventListener('change', handleFile);
116114

117115
checkbox.addEventListener('change', (e) => {
118116
const value = e.target.checked;
119-
console.log('duplicate points for face texture', value);
120117
window.location = `?duplicatePointsForFaceTexture=${value}`;
121-
refresh();
122118
});
123119

124120
// ----------------------------------------------------------------------------

Sources/IO/Geometry/PLYReader/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ interface IPLYReaderOptions {
1616
export interface IPLYReaderInitialValues {
1717
/**
1818
* Controls whether points are duplicated for face-texture mapping.
19+
* Default is true.
1920
*/
2021
duplicatePointsForFaceTexture?: boolean;
2122

2223
/**
2324
* The tolerance used to determine if two points are the same.
25+
* Default is 0.000001.
2426
*/
2527
faceTextureTolerance?: number;
2628
}

Sources/IO/Geometry/PLYReader/index.js

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ function postProcess(
184184
}
185185

186186
// Process face texture coordinates
187-
if (hasFaceTCoords && !hasVertTCoords) {
187+
if (hasFaceTCoords && !hasVertTCoords && nbFaces > 0) {
188+
// don't use array.shift, because buffer.indices will be used later
188189
let idxVerts = 0;
189190
let idxCoord = 0;
190191

@@ -230,8 +231,8 @@ function postProcess(
230231
// Check if we already have a point with these texture coordinates
231232
if (pointIds.has(key)) {
232233
const candidates = pointIds.get(key);
233-
// eslint-disable-next-line no-restricted-syntax
234-
for (const candidateId of candidates) {
234+
for (let i = 0, len = candidates.length; i < len; i++) {
235+
const candidateId = candidates[i];
235236
const samePosition =
236237
Math.abs(
237238
pointValues[candidateId * 3] - pointValues[vertId * 3]
@@ -350,14 +351,27 @@ function postProcess(
350351
}
351352

352353
const polydata = vtkPolyData.newInstance();
353-
354354
polydata.getPoints().setData(pointValues, 3);
355+
356+
// If we have faces, add them as polys
357+
if (nbFaces > 0) {
358+
polydata.getPolys().setData(Uint32Array.from(buffer.indices));
359+
} else {
360+
// Point cloud - create a vertex list containing all points
361+
const verts = new Uint32Array(nbVerts * 2);
362+
for (let i = 0; i < nbVerts; i++) {
363+
verts[i * 2] = 1; // number of points in vertex cell (always 1)
364+
verts[i * 2 + 1] = i; // point index
365+
}
366+
polydata.getVerts().setData(verts);
367+
}
368+
355369
if (hasColor) {
356370
polydata.getPointData().setScalars(
357371
vtkDataArray.newInstance({
358372
numberOfComponents: 3,
359373
values: colorArray,
360-
name: 'Scalars',
374+
name: 'RGB',
361375
})
362376
);
363377
}
@@ -383,8 +397,6 @@ function postProcess(
383397
);
384398
}
385399

386-
polydata.getPolys().setData(Uint32Array.from(buffer.indices));
387-
388400
return polydata;
389401
}
390402

@@ -472,7 +484,7 @@ function handleElement(buffer, name, element) {
472484

473485
if (vertexIndices && vertexIndices.length > 0) {
474486
buffer.indices.push(vertexIndices.length);
475-
vertexIndices.forEach((val) => {
487+
vertexIndices.forEach((val, idx) => {
476488
buffer.indices.push(val);
477489
});
478490
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import test from 'tape';
2+
import vtkPLYReader from 'vtk.js/Sources/IO/Geometry/PLYReader';
3+
4+
const plyFile = `ply
5+
format ascii 1.0
6+
comment Created by vtk.js with normals and UVs
7+
element vertex 8
8+
property float x
9+
property float y
10+
property float z
11+
property uchar red
12+
property uchar green
13+
property uchar blue
14+
property float nx
15+
property float ny
16+
property float nz
17+
property float u
18+
property float v
19+
element face 6
20+
property list uchar uint vertex_indices
21+
end_header
22+
2.000000 0.000000 -2.000000 255 255 0 0.577350 -0.577350 -0.577350 1.0 0.0
23+
2.000000 0.000000 0.000000 255 0 0 0.577350 -0.577350 0.577350 1.0 0.0
24+
0.000000 0.000000 0.000000 0 0 0 -0.577350 -0.577350 0.577350 0.0 0.0
25+
0.000000 0.000000 -2.000000 0 255 0 -0.577350 -0.577350 -0.577350 0.0 0.0
26+
2.000000 2.000000 -2.000000 255 255 255 0.577350 0.577350 -0.577350 1.0 1.0
27+
0.000000 2.000000 -2.000000 0 255 255 -0.577350 0.577350 -0.577350 0.0 1.0
28+
0.000000 2.000000 0.000000 0 0 255 -0.577350 0.577350 0.577350 0.0 1.0
29+
2.000000 2.000000 0.000000 255 0 255 0.577350 0.577350 0.577350 1.0 1.0
30+
4 0 1 2 3
31+
4 4 5 6 7
32+
4 0 4 7 1
33+
4 1 7 6 2
34+
4 2 6 5 3
35+
4 4 0 3 5`;
36+
37+
const pointCloudPLY = `ply
38+
format ascii 1.0
39+
element vertex 4
40+
property float x
41+
property float y
42+
property float z
43+
end_header
44+
0 0 0
45+
1 1 1
46+
2 2 2
47+
3 3 3`;
48+
49+
test('PLYReader: Parse ASCII PLY file', (t) => {
50+
const plyReader = vtkPLYReader.newInstance({
51+
duplicatePointsForFaceTexture: false,
52+
});
53+
plyReader.parseAsText(plyFile);
54+
const output = plyReader.getOutputData();
55+
t.equal(output.getNumberOfPoints(), 8, 'Should parse 8 vertices');
56+
t.deepEqual(
57+
output.getPoints().getData(),
58+
new Float32Array([
59+
2, 0, -2, 2, 0, 0, 0, 0, 0, 0, 0, -2, 2, 2, -2, 0, 2, -2, 0, 2, 0, 2, 2,
60+
0,
61+
]),
62+
'Should parse vertex positions correctly'
63+
);
64+
t.deepEqual(
65+
output.getPolys().getData(),
66+
new Uint32Array([
67+
4, 0, 1, 2, 3, 4, 4, 5, 6, 7, 4, 0, 4, 7, 1, 4, 1, 7, 6, 2, 4, 2, 6, 5, 3,
68+
4, 4, 0, 3, 5,
69+
]),
70+
'Should parse face indices correctly'
71+
);
72+
t.end();
73+
});
74+
75+
test('PLYReader: Parse Point Cloud PLY file', (t) => {
76+
const plyReader = vtkPLYReader.newInstance({
77+
duplicatePointsForFaceTexture: false,
78+
});
79+
plyReader.parseAsText(pointCloudPLY);
80+
const output = plyReader.getOutputData();
81+
t.equal(output.getNumberOfPoints(), 4, 'Should parse 4 vertices');
82+
t.deepEqual(
83+
output.getPoints().getData(),
84+
new Float32Array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]),
85+
'Should parse vertex positions correctly'
86+
);
87+
t.end();
88+
});
89+
90+
test('PLYReader: Parse PLY with color', (t) => {
91+
const plyReader = vtkPLYReader.newInstance({
92+
duplicatePointsForFaceTexture: false,
93+
});
94+
plyReader.parseAsText(plyFile);
95+
const output = plyReader.getOutputData();
96+
t.deepEqual(
97+
output.getPointData().getScalars().getData(),
98+
new Uint8Array([
99+
255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 255, 0, 255, 255, 255, 0, 255, 255, 0,
100+
0, 255, 255, 0, 255,
101+
]),
102+
'Should parse vertex colors correctly'
103+
);
104+
t.end();
105+
});
106+
107+
test('PLYReader: Parse PLY with normals', (t) => {
108+
const plyReader = vtkPLYReader.newInstance({
109+
duplicatePointsForFaceTexture: false,
110+
});
111+
plyReader.parseAsText(plyFile);
112+
const output = plyReader.getOutputData();
113+
t.deepEqual(
114+
output.getPointData().getNormals().getData(),
115+
new Float32Array([
116+
0.5773500204086304, -0.5773500204086304, -0.5773500204086304,
117+
0.5773500204086304, -0.5773500204086304, 0.5773500204086304,
118+
-0.5773500204086304, -0.5773500204086304, 0.5773500204086304,
119+
-0.5773500204086304, -0.5773500204086304, -0.5773500204086304,
120+
0.5773500204086304, 0.5773500204086304, -0.5773500204086304,
121+
-0.5773500204086304, 0.0, -0.5773500204086304, 0.0, 0.0,
122+
0.5773500204086304, 0.0, 0.0, 0.5773500204086304,
123+
]),
124+
'Should parse vertex normals correctly'
125+
);
126+
t.end();
127+
});
128+
129+
test('PLYReader: Parse PLY with texture coordinates', (t) => {
130+
const plyReader = vtkPLYReader.newInstance();
131+
plyReader.parse(plyFile);
132+
const output = plyReader.getOutputData();
133+
t.deepEqual(
134+
output.getPointData().getTCoords().getData(),
135+
new Float32Array([1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1]),
136+
'Should parse texture coordinates correctly'
137+
);
138+
t.end();
139+
});

0 commit comments

Comments
 (0)