Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
/uploads
/examples/output
185 changes: 185 additions & 0 deletions examples/api-example-multiple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import fetch from 'node-fetch';
import fs from 'fs/promises';
import FormData from 'form-data';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';

/**
* SVGnest API 使用示例 - 多个SVG文件版本
* 这个版本分别上传容器和零件的SVG文件
*/

// 获取当前文件的目录路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// 使用fetch调用API(在Node.js环境中)
async function runNestingWithMultipleSvgFiles(binFilePath, partsFilePath, config = {}) {
try {
// 确保使用绝对路径
const absoluteBinPath = path.isAbsolute(binFilePath) ? binFilePath : path.resolve(__dirname, binFilePath);
const absolutePartsPath = path.isAbsolute(partsFilePath) ? partsFilePath : path.resolve(__dirname, partsFilePath);

console.log(`读取bin文件: ${absoluteBinPath}`);
console.log(`读取parts文件: ${absolutePartsPath}`);

// 读取SVG文件
const binFile = await fs.readFile(absoluteBinPath);
const partsFile = await fs.readFile(absolutePartsPath);

// 创建FormData
const formData = new FormData();
formData.append('binFile', binFile, {
filename: path.basename(absoluteBinPath),
contentType: 'image/svg+xml',
});
formData.append('partsFile', partsFile, {
filename: path.basename(absolutePartsPath),
contentType: 'image/svg+xml',
});

// 添加配置参数
if (config.spacing) formData.append('spacing', config.spacing.toString());
if (config.rotations) formData.append('rotations', config.rotations.toString());
if (config.populationSize) formData.append('populationSize', config.populationSize.toString());
if (config.mutationRate) formData.append('mutationRate', config.mutationRate.toString());
if (config.useHoles) formData.append('useHoles', config.useHoles.toString());
if (config.exploreConcave) formData.append('exploreConcave', config.exploreConcave.toString());

// 第一步:创建嵌套任务(上传SVG文件)
console.log('上传bin文件和parts文件...');
const createResponse = await fetch('http://localhost:3000/api/jobs/upload-multiple', {
method: 'POST',
body: formData,
headers: formData.getHeaders(),
});

const createResult = await createResponse.json();

if (!createResponse.ok) {
throw new Error(`创建任务失败: ${createResult.error || createResponse.statusText}`);
}

const { jobId } = createResult;
console.log(`创建任务成功,ID: ${jobId}`);
console.log(`容器ID: ${createResult.bin.id}`);
console.log(`零件数量: ${createResult.partsCount}`);

// 第二步:启动嵌套计算
console.log('启动嵌套计算...');
const startResponse = await fetch(`http://localhost:3000/api/jobs/${jobId}/start`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
iterations: 20
})
});

if (!startResponse.ok) {
const startError = await startResponse.json();
throw new Error(`启动任务失败: ${startError.error || startResponse.statusText}`);
}

console.log(`已启动任务: ${jobId}`);

// 第三步:轮询任务状态
let status = 'processing';
while (status === 'processing') {
await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒

const statusResponse = await fetch(`http://localhost:3000/api/jobs/${jobId}`);
const jobStatus = await statusResponse.json();

console.log(`任务进度: ${Math.round(jobStatus.progress * 100)}%`);
status = jobStatus.status;
}

// 第四步:获取结果
if (status === 'completed') {
const resultResponse = await fetch(`http://localhost:3000/api/jobs/${jobId}/result`);
const { result } = await resultResponse.json();

console.log('嵌套完成!');
console.log(`利用率: ${(result.utilization * 100).toFixed(2)}%`);
console.log('零件放置情况:');

// 添加空数组检查,避免forEach错误
if (result.placements && Array.isArray(result.placements)) {
result.placements.forEach(placement => {
console.log(`- 零件 ${placement.partId}: 位置 (${placement.x.toFixed(2)}, ${placement.y.toFixed(2)}), 旋转 ${placement.rotation}°`);
});

// 下载SVG文件
await downloadSVG(jobId);
} else {
console.log('没有返回零件放置信息');
}

// 显示未放置的零件
if (result.unplacedParts && result.unplacedParts.length > 0) {
console.log('未放置的零件:');
result.unplacedParts.forEach(part => {
console.log(`- 零件 ${part.id || part}`);
});
}

return result;
} else {
console.error(`任务失败,状态: ${status}`);
return null;
}
} catch (error) {
console.error('执行嵌套出错:', error);
throw error;
}
}

/**
* 从服务器下载SVG文件
* @param {string} jobId - 任务ID
*/
async function downloadSVG(jobId) {
try {
console.log('从服务器下载SVG文件...');

const svgResponse = await fetch(`http://localhost:3000/api/jobs/${jobId}/svg`);

if (!svgResponse.ok) {
throw new Error(`下载SVG失败: ${svgResponse.status} ${svgResponse.statusText}`);
}

const svgContent = await svgResponse.text();

// 保存到文件
const outputDir = path.resolve(__dirname, 'output');

// Ensure output directory exists
try {
await fs.access(outputDir);
} catch (err) {
await fs.mkdir(outputDir, { recursive: true });
}

const filename = path.join(outputDir, `nesting-result-${jobId}.svg`);
await fs.writeFile(filename, svgContent);
console.log(`SVG文件已下载并保存为: ${filename}`);
} catch (error) {
console.error('下载SVG出错:', error);
}
}

// 执行示例
const binFilePath = process.argv[2] || './bin.svg';
const partsFilePath = process.argv[3] || './parts.svg';

console.log(`使用bin文件: ${binFilePath}`);
console.log(`使用parts文件: ${partsFilePath}`);

runNestingWithMultipleSvgFiles(binFilePath, partsFilePath, {
spacing: 5,
rotations: 4,
useHoles: true
}).catch(console.error);
5 changes: 5 additions & 0 deletions examples/bin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions examples/parts.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "svgnest-api",
"version": "1.0.0",
"description": "SVGnest API for optimal nesting of parts",
"main": "server.js",
"type": "module",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"example-multiple": "node examples/api-example-multiple.js",
"init-examples": "node src/scripts/init-examples.js",
"validate-svg": "node src/scripts/validate-svg.js"
},
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"form-data": "^4.0.0",
"js-clipper": "^1.0.1",
"multer": "^2.0.2",
"node-fetch": "^2.6.7",
"uuid": "^8.3.2",
"xmldom": "^0.6.0"
},
"devDependencies": {
"nodemon": "^2.0.14"
}
}
Loading