import * as fs from 'fs'; import * as path from 'path'; import * as db from './db.js'; import { samplePointcloudFile, type SampledPoint } from './pointcloudSample.js'; /** 与批量采样接口一致的配额 seek 采样(供热力分析作业复用) */ export async function sampleTaskPointclouds(taskId: number, budget: number): Promise { const pcRecords = await db.listTaskPointcloudFiles(taskId); const paths = pcRecords.map((r) => r.filePath); if (paths.length === 0) return []; const budgetClamped = Math.min(Math.max(50_000, budget), 20_000_000); const supportedExts = ['.ply', '.txt', '.xyz', '.pts']; const fileInfos = paths.map((fp) => ({ filePath: fp, name: path.basename(fp), ext: path.extname(fp).toLowerCase(), size: fs.existsSync(fp) ? fs.statSync(fp).size : 0, })); const supportedFiles = fileInfos.filter((f) => supportedExts.includes(f.ext) && f.size > 0); const totalBytes = supportedFiles.reduce((s, f) => s + f.size, 0); if (supportedFiles.length === 0) return []; const quotaMap = new Map(); for (const f of supportedFiles) { const quota = totalBytes > 0 ? Math.max(100, Math.round((f.size / totalBytes) * budgetClamped)) : Math.floor(budgetClamped / supportedFiles.length); quotaMap.set(f.filePath, quota); } const sampledPerFile = await Promise.all( fileInfos.map((f) => { if (!supportedExts.includes(f.ext) || f.size === 0) { return Promise.resolve([] as SampledPoint[]); } return samplePointcloudFile(f.filePath, f.size, quotaMap.get(f.filePath) ?? 0); }), ); const allPoints: SampledPoint[] = []; for (const pts of sampledPerFile) { allPoints.push(...pts); } return allPoints; }