import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import { samplePointcloudFile } from './pointcloudSample'; const temporaryDirectories: string[] = []; afterEach(() => { while (temporaryDirectories.length > 0) { rmSync(temporaryDirectories.pop()!, { recursive: true, force: true }); } }); function writeBinaryPly(properties: string[], records: number[][]): string { const directory = mkdtempSync(join(tmpdir(), 'pointcloud-sample-')); temporaryDirectories.push(directory); const filePath = join(directory, 'cloud.ply'); const header = Buffer.from([ 'ply', 'format binary_little_endian 1.0', `element vertex ${records.length}`, ...properties.map((name) => `property float ${name}`), 'end_header', '', ].join('\n'), 'ascii'); const body = Buffer.alloc(records.length * properties.length * 4); records.forEach((record, recordIndex) => { record.forEach((value, propertyIndex) => { body.writeFloatLE(value, (recordIndex * properties.length + propertyIndex) * 4); }); }); writeFileSync(filePath, Buffer.concat([header, body])); return filePath; } describe('samplePointcloudFile', () => { it('不会为只有 xyz 的输入点云伪造误差', async () => { const filePath = writeBinaryPly( ['x', 'y', 'z'], [[1, 2, 3], [4, 5, 6]], ); const points = await samplePointcloudFile(filePath, 1_000, 2); expect(points).toHaveLength(2); expect(points.every((point) => point.error === undefined)).toBe(true); }); it.each(['error', 'deviation'])('保留 PLY 中真实的 %s 属性', async (errorProperty) => { const filePath = writeBinaryPly( ['x', 'y', 'z', errorProperty], [[1, 2, 3, 0.25], [4, 5, 6, -0.5]], ); const points = await samplePointcloudFile(filePath, 1_000, 2); expect(points.map((point) => point.error)).toEqual([0.25, -0.5]); }); });