Hachura SVG: crie caminhos reais para plotters de caneta
Gere caminhos abertos de hachura a partir de uma área SVG fechada. Reproduza verificações de furos, espaçamento, ângulo e arquivo salvo com Paper.js.
Nesta página
- 1. Escolha uma área para preencher, não uma linha para recuperar
- 2. Diferencie a aparência recortada dos caminhos aparados
- 3. Execute o exemplo completo com Paper.js
- 4. Confira juntos espaçamento, ângulo e escala física
- 5. Separe intervalos entre trajetos de um plano otimizado
- Corrija falhas comuns de geometria
- Perguntas frequentes
- Fontes
Para preencher um SVG com hachuras para um plotter de caneta, gere linhas abertas dentro da área fechada desejada, divida-as nos limites e exporte os segmentos restantes como geometria. Antes de preparar o trabalho da máquina, confira se o arquivo salvo contém trajetos que cruzam furos ou saem da forma.
Um preenchimento colorido ou uma prévia listrada não fornece, por si só, esses trajetos de desenho. Este tutorial cria um emblema original de cinco lados com um furo retangular, usa Paper.js para intersectar linhas abertas com essa área e reabre os SVGs salvos para inspeção. As verificações comprovam a geometria digital das linhas centrais, não uma plotagem física bem-sucedida.
1. Escolha uma área para preencher, não uma linha para recuperar
Você precisa de uma região fechada cujos furos façam parte da geometria. Um retângulo branco sobre uma forma sólida pode parecer um furo e ainda deixar a região subjacente intacta. A documentação de preenchimento com hachuras do GRBL-Plotter ilustra essa distinção entre objetos sobrepostos e uma abertura dentro de um único caminho. Não executamos o GRBL-Plotter aqui.
Se você quer recuperar um único traço no meio de uma linha raster grossa, use o guia de SVG de linha única. A hachura cria vários traços em uma área; não recupera a linha central original da caneta.
Preserve uma cópia da arte preenchida. O exemplo produz deliberadamente trajetos de hachura sem adicionar uma passada de contorno. Se você também quiser desenhar a borda externa ou a borda do furo, prepare-as como trajetos separados e intencionais.
2. Diferencie a aparência recortada dos caminhos aparados
Um pattern SVG pinta um ladrilho repetido. Um caminho de recorte restringe a região renderizada sem mudar a geometria subjacente. Ambos podem criar uma imagem útil na tela, mas o aplicativo ainda precisa de um método para transformar essa aparência em trajetos de caneta.
Nosso controle apenas com recorte armazena dez linhas horizontais de 200 unidades de usuário cada. O recorte esconde as partes externas e a abertura retangular. O comprimento das linhas armazenadas ainda soma 2.000 unidades.
A exportação aparada com espaçamento maior contém 14 caminhos abertos separados, somando 1.320 unidades. Não há caminho de recorte, padrão, máscara, imagem incorporada ou símbolo reutilizado. Quatro fileiras de hachura se dividem em dois caminhos ao redor do furo. Essa diferença pode ser inspecionada no SVG salvo, mesmo quando as prévias recortada e aparada parecem iguais.

O guia de unidades de pattern em SVG aborda a pintura repetida quando esse é o resultado desejado. Aqui, a saída necessária é uma geometria aberta explícita.
3. Execute o exemplo completo com Paper.js
Use um navegador, um editor de texto e a compilação completa do Paper.js 0.12.18 da versão oficial. Coloque paper-full.min.js em uma pasta library. Salve este HTML como generate.html e o JavaScript seguinte como hatch.js ao lado dessa pasta. O emblema original está incluído no código.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Generate original hatch routes</title>
<canvas id="work" width="200" height="140" hidden></canvas>
<p id="status">Running</p>
<pre id="export-receipt"></pre>
<h2>0 degrees, 10-unit spacing</h2>
<img id="preview-coarse" width="400" alt="Coarse hatch">
<pre id="svg-coarse"></pre>
<h2>0 degrees, 5-unit spacing</h2>
<img id="preview-dense" width="400" alt="Dense hatch">
<pre id="svg-dense"></pre>
<h2>30 degrees, 10-unit spacing</h2>
<img id="preview-angled" width="400" alt="Angled hatch">
<pre id="svg-angled"></pre>
<script src="library/paper-full.min.js"></script>
<script src="hatch.js"></script>
</html>paper.setup(document.getElementById('work'));
// Original area: a five-sided boundary with a rectangular hole.
const areaData = 'M20 20 L140 20 L180 60 L180 120 L20 120 Z ' +
'M60 50 L60 90 L110 90 L110 50 Z';
const configurations = [
{ id: 'coarse', angle: 0, spacing: 10 },
{ id: 'dense', angle: 0, spacing: 5 },
{ id: 'angled', angle: 30, spacing: 10 }
];
function makeHatch(angle, spacing) {
const area = new paper.CompoundPath({
pathData: areaData,
fillRule: 'nonzero',
insert: false
});
const radians = angle * Math.PI / 180;
const direction = new paper.Point(Math.cos(radians), Math.sin(radians));
const normal = new paper.Point(-direction.y, direction.x);
const center = new paper.Point(100, 70);
const delivery = new paper.Group({ insert: false });
for (let offset = -200 + spacing / 2; offset < 200; offset += spacing) {
const origin = center.add(normal.multiply(offset));
const line = new paper.Path.Line({
from: origin.subtract(direction.multiply(300)),
to: origin.add(direction.multiply(300)),
insert: false
});
const kept = line.intersect(area, { trace: false, insert: false });
const parts = kept instanceof paper.CompoundPath ? [...kept.children] : [kept];
const routes = parts.filter(path => path.length > 1e-7);
for (const path of routes) {
if (path.closed || path.segments.length !== 2) {
throw new Error('Expected one open straight segment');
}
if (path.firstSegment.point.dot(direction) > path.lastSegment.point.dot(direction)) {
path.reverse();
}
}
routes.sort((a, b) => a.firstSegment.point.dot(direction) - b.firstSegment.point.dot(direction));
for (const path of routes) {
path.fillColor = null;
path.strokeColor = '#2f6977';
path.strokeWidth = 1;
path.strokeCap = 'butt';
delivery.addChild(path);
}
if (kept instanceof paper.CompoundPath || routes.length === 0) {
kept.remove();
}
line.remove();
}
area.remove();
const geometry = delivery.exportSVG({ asString: true, precision: 6 });
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="100mm" height="70mm" ' +
'viewBox="0 0 200 140">' + geometry + '</svg>';
const routeCount = delivery.children.length;
const drawnLength = delivery.children.reduce((sum, path) => sum + path.length, 0);
let transitionLength = 0;
for (let index = 1; index < routeCount; index += 1) {
transitionLength += delivery.children[index - 1].lastSegment.point
.getDistance(delivery.children[index].firstSegment.point);
}
delivery.remove();
return { svg, routeCount, drawnLength, transitionLength };
}
const receipt = { paper: paper.version, units: 'viewBox user units; 0.5 mm per unit', rows: [] };
for (const configuration of configurations) {
const result = makeHatch(configuration.angle, configuration.spacing);
document.getElementById('svg-' + configuration.id).textContent = result.svg;
const preview = document.getElementById('preview-' + configuration.id);
preview.src = URL.createObjectURL(new Blob([result.svg], { type: 'image/svg+xml' }));
receipt.rows.push({ ...configuration, route_count: result.routeCount,
drawn_length: result.drawnLength, transition_length: result.transitionLength });
}
document.getElementById('export-receipt').textContent = JSON.stringify(receipt, null, 2);
document.getElementById('status').textContent = 'Generated. Save each exact SVG shown below, then reopen those saved files for independent checks.';Abra generate.html no navegador. Executamos o exemplo com Paper.js 0.12.18 no Chromium 155. Cada resultado mostra uma prévia e sua marcação SVG exata. Copie as três strings SVG completas para hatch-coarse.svg, hatch-dense.svg e hatch-angled.svg, e depois abra esses arquivos salvos separadamente.
A chamada essencial é line.intersect(area, { trace: false, insert: false }). O Paper.js documenta trace: false como a divisão do primeiro caminho nas interseções e a retenção de suas curvas dentro da área do segundo. Isso preserva os segmentos abertos da hachura. O modo padrão de traçado de área responde a outra questão booleana; o guia de formas preenchidas no Paper.js trata desse fluxo.
A função define fillColor como null e atribui um traço a cada trajeto. Também verifica se os resultados são abertos e têm dois pontos antes de exportar. A área fechada continua sendo um auxiliar de construção e não entra no grupo de entrega.
4. Confira juntos espaçamento, ângulo e escala física
As três exportações declaram width="100mm", height="70mm" e viewBox="0 0 200 140". Isso mapeia uma unidade de usuário para 0,5 mm no SVG. Um espaçamento de hachura de 10 unidades representa, portanto, 5 mm; 5 unidades representam 2,5 mm.
Esses espaçamentos deliberadamente amplos facilitam a inspeção dos trajetos individuais. Não são recomendações para cobertura sólida de tinta. O traço na tela tem uma unidade de usuário de largura, mas sua aparência não mede a marca de uma caneta real em um material.
O laço de deslocamento fixa a fase da hachura na metade de um espaçamento. Mudar essa fase pode alterar quais detalhes do limite uma linha encontra. Em +30°, a direção desce para a direita neste sistema de coordenadas SVG. O espaçamento é medido perpendicularmente às linhas paralelas, não como sua separação vertical na tela.
| Arquivo salvo | Ângulo | Espaçamento | Trajetos abertos | Comprimento desenhado da linha central |
|---|---|---|---|---|
hatch-coarse.svg | 0° | 10 unidades / 5 mm | 14 | 1.320 unidades / 660 mm |
hatch-dense.svg | 0° | 5 unidades / 2,5 mm | 28 | 2.640 unidades / 1.320 mm |
hatch-angled.svg | 30° | 10 unidades / 5 mm | 20 | Cerca de 1.318,564 unidades / 659,282 mm |
Calculamos as interseções esperadas entre linhas e polígono de forma independente e comparamos as coordenadas salvas com tolerância de 0,000002 unidade de usuário. Todos os trajetos exportados corresponderam aos extremos previstos. Cada trajeto era aberto, e nenhum ponto amostrado de linha central cruzou o furo ou o exterior além dessa tolerância. Os três SVGs salvos preservaram o mapeamento de tamanho físico declarado.
Para outra forma, confira os trajetos realmente exportados em vez de presumir que as contagens desta amostra se repetirão. Curvas, espaços muito pequenos, autointerseções, grupos transformados e documentos SVG importados arbitrários estão fora do escopo testado.
5. Separe intervalos entre trajetos de um plano otimizado
O arquivo com espaçamento maior tem 14 trajetos abertos e 13 intervalos entre trajetos consecutivos em sua ordem serializada. Conectar esses intervalos com a caneta abaixada adicionaria marcas indesejadas. Seu fluxo de plotagem precisa tratar as transições conforme o objetivo antes de desenhar.
O script mantém uma ordem simples em um só sentido: percorre os deslocamentos da hachura em sequência e segue todos os segmentos na mesma direção. Somar as distâncias retas do fim de cada trajeto ao início do seguinte dá cerca de 1.562,997 unidades de usuário no arquivo com espaçamento maior. O arquivo mais denso tem 27 intervalos e soma de conexões de aproximadamente 3.281,578 unidades.
Essas somas excluem a aproximação inicial e o retorno final. São consequências geométricas dessa ordem, não deslocamento medido da máquina, estimativa de tempo ou plano otimizado. Um aplicativo de plotagem pode inverter ou reordenar os trajetos. Reduzir o espaçamento pela metade dobrou o comprimento desenhado e a quantidade de trajetos desta amostra; não comprovou uma duplicação universal do tempo de trabalho.
Reabra o arquivo no aplicativo que controlará seu plotter, verifique o tamanho e inspecione a prévia dos trajetos e o levantamento da caneta. Teste caneta, material, espaçamento, folga nas bordas e configurações de movimento no seu próprio equipamento. Não executamos um trabalho de plotter, geramos código de máquina ou testamos Cricut Draw/Attach.
Corrija falhas comuns de geometria
| Sintoma | O que inspecionar e mudar |
|---|---|
| A hachura cruza o furo pretendido | Inspecione a região preenchida. Substitua uma sobreposição branca por uma abertura real na geometria composta antes de intersectar. |
| A prévia está correta, mas linhas inteiras permanecem | Procure dependência de recorte ou padrão. Inspecione as coordenadas salvas dos trajetos em vez de confiar na silhueta renderizada. |
| As linhas viram formas fechadas | Confirme que o primeiro operando é uma linha aberta e que trace: false está presente. O exemplo rejeita resultados fechados ou com vários segmentos. |
| A tinta ultrapassa um limite | Diferencie a linha central da área ocupada pelo traço. O exemplo não recua a área pelo raio da ponta; prepare a folga adequada e teste a ferramenta real. |
| O preenchimento fica esparso ou a superfície molha demais | Ajuste com uma amostra controlada de caneta e material. O espaçamento digital sozinho não comprova cobertura ou comportamento de secagem. |
| O arquivo faz saltos inesperados | Inspecione ordem e transições com a caneta levantada no software de destino. Preserve os trajetos originais antes de otimizar; depois confira novamente furos e limites. |
Se a origem for apenas uma silhueta raster plana, o fluxo de clipart para SVG do PerfectVector pode ajudar a recuperar primeiro a arte fechada. Inspecione o contorno e cada abertura, depois gere os trajetos de hachura a partir da geometria aceita. Uma arte vetorial existente e limpa pode ir diretamente à preparação da hachura.
Vetorizar a silhueta e gerar os trajetos de hachura são etapas separadas. O emblema original deste artigo foi criado como geometria vetorial; não é uma amostra de conversão do PerfectVector.
Perguntas frequentes
Um preenchimento SVG vira hachura de caneta automaticamente? Não. Um preenchimento descreve a aparência pintada. Um fluxo de plotagem precisa de trajetos de hachura ou de outro método suportado para criar traços de desenho a partir da área.
Por que minha prévia de hachura ainda contém linhas passando pelo furo? Um recorte pode esconder partes de linhas completas sem aparar a geometria armazenada. Confira as coordenadas salvas e faça o furo pertencer à área real antes de produzir trajetos explícitos.
Qual espaçamento de hachura devo usar? Escolha com um teste de caneta e material na escala pretendida. Este exemplo digital usa espaçamentos de 5 mm e 2,5 mm para tornar os trajetos visíveis; não comprova cobertura sólida nem uma configuração universal de caneta.
Isso é um único caminho contínuo? Não. O resultado com espaçamento maior contém 14 trajetos abertos separados. Seu furo divide quatro fileiras, e o fluxo de desenho precisa tratar os intervalos sem adicionar marcas indesejadas.
Fontes
- Interseção PathItem do Paper.js — Documenta a divisão de caminhos abertos com trace false.
- Patterns SVG — Define a pintura repetida separadamente da geometria explícita dos trajetos.
- Máscaras CSS e caminhos de recorte — Explica por que o recorte muda a renderização sem aparar a forma subjacente.
- Preenchimento com hachuras no GRBL-Plotter — Mostra a distinção entre objetos sobrepostos e um furo dentro de um caminho.
- Versão Paper.js 0.12.18 — Identifica a versão da biblioteca usada no exemplo executável.
Se uma silhueta disponível só como raster precisar de limites editáveis, prepare uma candidata SVG de clipart, inspecione seus furos e gere e verifique os trajetos de hachura separadamente antes de testar o trabalho físico.
Mais do blog

Linhas de vinco e corte SVG: entenda a geometria
Leia a geometria da dobra SVG antes de definir Score ou Cut. Compare caminhos abertos, faixas preenchidas e tracejados e confira o fluxo documentado da Cricut.

Estêncil para gravar vidro: escolha o que remover
Defina quais regiões da máscara de vidro ficam protegidas ou expostas. Confira um emblema, ilhas internas e layouts complementares nos limites do fornecedor.
