@linkiez/glory-star-calculator
Version:
Calculadora de tempo de corte para arquivos SVG da máquina GloryStar_GS3015
371 lines • 12.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processSvg = processSvg;
exports.convertElementsToMovements = convertElementsToMovements;
const svg_parser_1 = require("svg-parser");
const types_1 = require("./types");
/**
* Processa uma string SVG e extrai os elementos
* @param svgContent Conteúdo do SVG em string
* @returns Array de elementos SVG processados
*/
function processSvg(svgContent) {
try {
// Validação básica se a entrada parece ser um SVG
if (!svgContent || typeof svgContent !== 'string') {
throw new Error('Conteúdo SVG inválido ou vazio');
}
// Verificação simplificada se o conteúdo contém pelo menos uma tag SVG
if (!svgContent.includes('<svg') || !svgContent.includes('>')) {
throw new Error('Conteúdo não parece ser um SVG válido');
}
const parsed = (0, svg_parser_1.parse)(svgContent);
// Verifica se o parser retornou algo utilizável
if (!parsed || typeof parsed !== 'object') {
throw new Error('Falha ao processar SVG: resultado de parsing inválido');
}
const elements = [];
// Função recursiva para processar nós SVG
function processNode(node) {
if (node.type === 'element') {
const element = processElement(node);
if (element) {
elements.push(element);
}
}
if (node.children && Array.isArray(node.children)) {
for (const child of node.children) {
processNode(child);
}
}
}
// Inicia o processamento recursivo
processNode(parsed);
return elements;
}
catch (error) {
throw new Error(`Erro ao processar o SVG: ${error}`);
}
}
/**
* Processa um elemento SVG
* @param element Elemento SVG do parser
* @returns Elemento SVG processado
*/
function processElement(element) {
switch (element.tagName) {
case 'line':
return processLine(element);
case 'polyline':
return processPolyline(element);
case 'polygon':
return processPolygon(element);
case 'path':
return processPath(element);
case 'circle':
return processCircle(element);
case 'rect':
return processRect(element);
case 'ellipse':
return processEllipse(element);
default:
return null;
}
}
/**
* Processa uma linha SVG
*/
function processLine(element) {
const { x1, y1, x2, y2 } = element.properties;
return {
type: types_1.SVGElementType.Line,
points: [
{ x: parseFloat(x1), y: parseFloat(y1) },
{ x: parseFloat(x2), y: parseFloat(y2) }
],
isClosed: false
};
}
/**
* Processa uma polilinha SVG
*/
function processPolyline(element) {
const points = parsePointsString(element.properties.points);
return {
type: types_1.SVGElementType.Polyline,
points,
isClosed: false
};
}
/**
* Processa um polígono SVG
*/
function processPolygon(element) {
const points = parsePointsString(element.properties.points);
return {
type: types_1.SVGElementType.Polygon,
points,
isClosed: true
};
}
/**
* Processa um path SVG
*/
function processPath(element) {
// Implementação simplificada - em um caso real, precisaríamos de um parser completo de SVG path
// Esta é apenas uma demonstração e deve ser substituída por uma solução robusta
const d = element.properties.d || '';
const points = [];
// Implementação básica para demonstração
// Apenas extrai coordenadas simples de comandos M, L, H, V
const commands = d.match(/[MLHVZmlhvz][^MLHVZmlhvz]*/g) || [];
let currentPoint = { x: 0, y: 0 };
commands.forEach((cmd) => {
const type = cmd[0];
const argsStr = cmd.slice(1).trim();
const args = argsStr ? argsStr.split(/[\s,]+/).map(parseFloat).filter(n => !isNaN(n)) : [];
switch (type) {
case 'M': // Move to
if (args.length >= 2) {
currentPoint = { x: args[0], y: args[1] };
points.push(Object.assign({}, currentPoint));
// Se houver coordenadas adicionais após M, elas são tratadas como L
for (let i = 2; i < args.length; i += 2) {
if (i + 1 < args.length) {
currentPoint = { x: args[i], y: args[i + 1] };
points.push(Object.assign({}, currentPoint));
}
}
}
break;
case 'L': // Line to
for (let i = 0; i < args.length; i += 2) {
if (i + 1 < args.length) {
currentPoint = { x: args[i], y: args[i + 1] };
points.push(Object.assign({}, currentPoint));
}
}
break;
case 'H': // Horizontal line
for (const x of args) {
currentPoint = { x, y: currentPoint.y };
points.push(Object.assign({}, currentPoint));
}
break;
case 'V': // Vertical line
for (const y of args) {
currentPoint = { x: currentPoint.x, y };
points.push(Object.assign({}, currentPoint));
}
break;
case 'Z': // Close path
case 'z':
if (points.length > 0) {
// Adiciona o ponto inicial para fechar o caminho
points.push(Object.assign({}, points[0]));
}
break;
}
});
return {
type: types_1.SVGElementType.Path,
points,
isClosed: d.toUpperCase().includes('Z')
};
}
/**
* Processa um círculo SVG convertendo-o para uma aproximação poligonal
*/
function processCircle(element) {
const cx = parseFloat(element.properties.cx || 0);
const cy = parseFloat(element.properties.cy || 0);
const r = parseFloat(element.properties.r || 0);
// Cria uma aproximação do círculo com pontos
const points = approximateCircle(cx, cy, r);
return {
type: types_1.SVGElementType.Circle,
points,
isClosed: true
};
}
/**
* Processa uma elipse SVG convertendo-a para uma aproximação poligonal
*/
function processEllipse(element) {
const cx = parseFloat(element.properties.cx || 0);
const cy = parseFloat(element.properties.cy || 0);
const rx = parseFloat(element.properties.rx || 0);
const ry = parseFloat(element.properties.ry || rx); // Se apenas rx for especificado
// Cria uma aproximação da elipse com pontos
const points = approximateEllipse(cx, cy, rx, ry);
return {
type: types_1.SVGElementType.Ellipse,
points,
isClosed: true
};
}
/**
* Processa um retângulo SVG
*/
function processRect(element) {
const x = parseFloat(element.properties.x || 0);
const y = parseFloat(element.properties.y || 0);
const width = parseFloat(element.properties.width || 0);
const height = parseFloat(element.properties.height || 0);
const rx = parseFloat(element.properties.rx || 0);
const ry = parseFloat(element.properties.ry || rx); // Se apenas rx for especificado
let points;
if (rx > 0 || ry > 0) {
// Retângulo com cantos arredondados
points = approximateRoundedRect(x, y, width, height, rx, ry);
}
else {
// Retângulo regular
points = [
{ x, y },
{ x: x + width, y },
{ x: x + width, y: y + height },
{ x, y: y + height },
{ x, y } // Fecha o caminho
];
}
return {
type: types_1.SVGElementType.Rect,
points,
isClosed: true
};
}
/**
* Analisa uma string de pontos SVG
*/
function parsePointsString(pointsString) {
if (!pointsString)
return [];
// Divide a string em pares de coordenadas
const pairs = pointsString.trim().split(/[\s,]+/);
const points = [];
for (let i = 0; i < pairs.length; i += 2) {
if (i + 1 < pairs.length) {
const x = parseFloat(pairs[i]);
const y = parseFloat(pairs[i + 1]);
if (!isNaN(x) && !isNaN(y)) {
points.push({ x, y });
}
}
}
return points;
}
/**
* Cria uma aproximação de um círculo como uma série de pontos
*/
function approximateCircle(cx, cy, r, segments = 36) {
const points = [];
const angleStep = (2 * Math.PI) / segments;
for (let i = 0; i <= segments; i++) {
const angle = i * angleStep;
points.push({
x: cx + r * Math.cos(angle),
y: cy + r * Math.sin(angle)
});
}
return points;
}
/**
* Cria uma aproximação de uma elipse como uma série de pontos
*/
function approximateEllipse(cx, cy, rx, ry, segments = 36) {
const points = [];
const angleStep = (2 * Math.PI) / segments;
for (let i = 0; i <= segments; i++) {
const angle = i * angleStep;
points.push({
x: cx + rx * Math.cos(angle),
y: cy + ry * Math.sin(angle)
});
}
return points;
}
/**
* Cria uma aproximação de um retângulo com cantos arredondados
*/
function approximateRoundedRect(x, y, width, height, rx, ry) {
// Limitar os raios aos limites do retângulo
rx = Math.min(rx, width / 2);
ry = Math.min(ry, height / 2);
const points = [];
const segments = 8; // Segmentos por canto
// Função auxiliar para adicionar um arco
function addArc(centerX, centerY, startAngle, endAngle) {
const angleStep = (endAngle - startAngle) / segments;
for (let i = 0; i <= segments; i++) {
const angle = startAngle + i * angleStep;
points.push({
x: centerX + rx * Math.cos(angle),
y: centerY + ry * Math.sin(angle)
});
}
}
// Canto superior direito
addArc(x + width - rx, y + ry, -Math.PI / 2, 0);
// Canto inferior direito
addArc(x + width - rx, y + height - ry, 0, Math.PI / 2);
// Canto inferior esquerdo
addArc(x + rx, y + height - ry, Math.PI / 2, Math.PI);
// Canto superior esquerdo
addArc(x + rx, y + ry, Math.PI, 3 * Math.PI / 2);
// Fecha o caminho adicionando o primeiro ponto novamente
points.push(Object.assign({}, points[0]));
return points;
}
/**
* Converte elementos SVG processados em movimentos
* @param elements Elementos SVG processados
* @returns Lista de movimentos
*/
function convertElementsToMovements(elements) {
const movements = [];
if (elements.length === 0) {
return movements;
}
let currentPosition = { x: 0, y: 0 };
elements.forEach(element => {
const points = element.points;
if (points.length === 0) {
return; // Pula elementos sem pontos
}
// Movimento de posicionamento até o primeiro ponto do elemento
movements.push({
start: currentPosition,
end: points[0],
isCutting: false
});
currentPosition = points[0];
// Processar os movimentos de corte entre pontos
for (let i = 1; i < points.length; i++) {
movements.push({
start: currentPosition,
end: points[i],
isCutting: true
});
currentPosition = points[i];
}
// Se o elemento for fechado e tivermos mais de um ponto,
// verificamos se o último ponto já é igual ao primeiro
if (element.isClosed && points.length > 1) {
const firstPoint = points[0];
const lastPoint = points[points.length - 1];
// Se o último ponto não é igual ao primeiro, adicionamos um movimento para fechar
if (Math.abs(firstPoint.x - lastPoint.x) > 0.001 ||
Math.abs(firstPoint.y - lastPoint.y) > 0.001) {
movements.push({
start: currentPosition,
end: firstPoint,
isCutting: true
});
currentPosition = firstPoint;
}
}
});
return movements;
}
//# sourceMappingURL=svgProcessor.js.map