PerfectVector
Par Irene Kim

Hachures SVG : créer de vrais chemins pour traceur

Génère des chemins de hachure ouverts dans une zone SVG fermée. Reproduis les contrôles de trou, d’espacement, d’angle et de fichier enregistré avec un exemple original Paper.js.

12 min de lecture
Sur cette page

Pour remplir un SVG de hachures destinées à un traceur à stylo, génère des lignes ouvertes dans la zone fermée prévue, coupe-les à ses limites et exporte les segments restants comme géométrie. Vérifie dans le fichier enregistré qu’aucun parcours ne traverse les trous ou ne sort de la forme avant de préparer le travail machine.

Un fond coloré ou un aperçu rayé ne fournit pas à lui seul ces parcours de dessin. Ce tutoriel construit un emblème original à cinq côtés avec un trou rectangulaire, utilise Paper.js pour intersecter des lignes ouvertes avec cette zone et rouvre les SVG enregistrés pour inspection. Les contrôles établissent une géométrie numérique de lignes médianes, pas un tracé physique réussi.

1. Choisis une zone à remplir, pas une ligne à retrouver

Il te faut une région fermée dont les trous font partie de la géométrie. Un rectangle blanc au-dessus d’une forme pleine peut sembler être un trou tout en laissant intacte la région sous-jacente. La documentation de remplissage par hachures de GRBL-Plotter illustre cette différence entre objets superposés et ouverture dans un seul chemin. Nous n’avons pas exécuté GRBL-Plotter ici.

Si tu souhaites retrouver un seul trait au milieu d’une épaisse ligne matricielle, utilise le guide du SVG à ligne unique. Le hachurage crée plusieurs traits à travers une zone ; il ne retrouve pas la ligne médiane d’un stylo d’origine.

Conserve une copie de l’illustration remplie. L’exemple produit volontairement des parcours de hachure sans ajouter de passage de contour. Si tu veux aussi dessiner la limite extérieure ou celle du trou, prépare-les comme parcours distincts et délibérés.

2. Distingue l’apparence rognée des chemins découpés

Un motif SVG peint une tuile répétée. Un chemin de découpe restreint la région rendue sans changer la géométrie sous-jacente. Les deux peuvent produire une image écran utile, mais une application a encore besoin d’un moyen de transformer cette apparence en parcours de stylo.

Notre témoin uniquement rogné stocke dix lignes horizontales de 200 unités utilisateur chacune. Le découpage masque leurs parties extérieures et l’ouverture rectangulaire. Les longueurs de lignes enregistrées totalisent toujours 2000 unités.

L’export grossier aux chemins découpés contient 14 chemins ouverts distincts totalisant 1320 unités. Il ne possède ni chemin de découpe, ni motif, ni masque, ni image incorporée, ni symbole réutilisé. Quatre rangées de hachures se divisent en deux chemins autour du trou. Cette différence est inspectable dans le SVG enregistré, même si les aperçus rogné et découpé se ressemblent.

Véritables SVG rouverts comparant un aperçu de hachures uniquement rogné à des chemins ouverts explicites à deux espacements et à un angle de 30 degrés
Exemple numérique original et véritables exports Paper.js. Le trou reste ouvert dans les trois fichiers de parcours. Les contrôles concernent les lignes médianes ; aucun traceur ni stylo physique n’a été testé.

Le guide des unités de motif SVG traite de la peinture répétée lorsqu’elle est la sortie souhaitée. Ici, la sortie requise est une géométrie ouverte explicite.

3. Exécute l’exemple Paper.js complet

Utilise un navigateur, un éditeur de texte et la version complète Paper.js 0.12.18 de la publication officielle. Place paper-full.min.js dans un dossier library. Enregistre ce HTML sous generate.html et le JavaScript suivant sous hatch.js à côté de ce dossier. L’emblème original est inclus dans le code.

<!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.';

Ouvre generate.html dans le navigateur. Nous l’avons exécuté avec Paper.js 0.12.18 dans Chromium 155. Chaque résultat affiche un aperçu et son balisage SVG exact. Copie les trois chaînes SVG complètes dans hatch-coarse.svg, hatch-dense.svg et hatch-angled.svg, puis ouvre ces fichiers enregistrés indépendamment.

L’appel essentiel est line.intersect(area, { trace: false, insert: false }). Paper.js documente trace: false comme découpant le premier chemin aux intersections et conservant ses courbes à l’intérieur de la zone du second chemin. Cela garde les segments de hachure ouverts. Le mode de traçage de zone par défaut répond à une autre question booléenne ; le guide Paper.js des formes remplies couvre ce processus.

La fonction met fillColor à null et donne un contour à chaque parcours. Elle vérifie aussi que les résultats sont ouverts et comportent deux points avant l’export. La zone fermée reste une aide à la construction et n’est pas incluse dans le groupe de livraison.

4. Vérifie ensemble espacement, angle et échelle physique

Les trois exports déclarent width="100mm", height="70mm" et viewBox="0 0 200 140". Cela fait correspondre une unité utilisateur à 0.5 mm dans le SVG. Un espacement de hachure de 10 unités représente donc 5 mm ; 5 unités représentent 2.5 mm.

Ces espacements d’exemple volontairement larges facilitent l’inspection des parcours individuels. Ce ne sont pas des recommandations de couverture d’encre uniforme. Le contour écran mesure une unité utilisateur de large, mais son apparence ne mesure pas la marque d’un véritable stylo sur un matériau.

La boucle de décalage fixe la phase de hachure à un demi-espacement. Changer cette phase peut modifier les particularités de limite rencontrées par une ligne. À 30° positifs, la direction descend vers la droite dans ce système de coordonnées SVG. L’espacement se mesure perpendiculairement aux lignes parallèles, et non comme leur séparation verticale à l’écran.

Fichier enregistréAngleEspacementParcours ouvertsLongueur de ligne médiane dessinée
hatch-coarse.svg0°10 unités / 5 mm141320 unités / 660 mm
hatch-dense.svg0°5 unités / 2.5 mm282640 unités / 1320 mm
hatch-angled.svg30°10 unités / 5 mm20Environ 1318.564 unités / 659.282 mm

Nous avons calculé indépendamment les intersections attendues entre lignes et polygones, puis comparé les coordonnées enregistrées avec une tolérance de 0.000002 unité utilisateur. Chaque parcours exporté correspondait à ses extrémités prévues. Chaque parcours était ouvert, et aucun point échantillonné de ligne médiane ne traversait le trou ou l’extérieur au-delà de cette tolérance. Les trois SVG enregistrés conservaient la correspondance de taille physique déclarée.

Pour une autre forme, vérifie les véritables parcours exportés au lieu de supposer que les nombres de cet exemple s’appliquent. Les courbes, très petits espaces, auto-intersections, groupes transformés et documents SVG importés quelconques sont hors du périmètre de test de cet exemple.

5. Sépare les intervalles entre parcours d’un trajet optimisé

Le fichier grossier possède 14 parcours ouverts et 13 intervalles entre parcours consécutifs dans son ordre sérialisé. Relier ces intervalles avec le stylo abaissé ajouterait des marques indésirables. Ton processus de tracé doit gérer les transitions comme prévu avant de dessiner.

Le script conserve un ordre simple à sens unique : il visite les décalages de hachure en séquence et suit chaque segment dans le même sens. Additionner les distances droites entre la fin d’un parcours et le début du suivant donne environ 1562.997 unités utilisateur pour le fichier grossier. Le fichier plus dense possède 27 intervalles et une somme de liaisons d’environ 3281.578 unités.

Ces sommes excluent l’approche initiale et le retour final. Ce sont des conséquences géométriques de cet ordre, pas un déplacement machine mesuré, une estimation de temps ou un trajet optimisé. Une application de tracé peut inverser ou réordonner les parcours. Diviser l’espacement par deux a doublé la longueur dessinée et le nombre de parcours de cet exemple ; cela n’établit pas un doublement universel du temps de travail.

Rouvre le fichier dans l’application qui pilotera ton traceur, vérifie sa taille et inspecte l’aperçu des parcours et le comportement de levée du stylo. Teste le stylo, le matériau, l’espacement, le dégagement de bord et les réglages de mouvement sur ta propre installation. Nous n’avons ni exécuté de travail de traceur, ni généré de code machine, ni testé Draw/Attach de Cricut.

Corrige les défauts géométriques courants

SymptômeCe qu’il faut inspecter et changer
Les hachures traversent le trou prévuInspecte la région remplie. Remplace une superposition blanche par une véritable ouverture dans la géométrie composée avant l’intersection.
L’aperçu est correct, mais les lignes entières restentCherche une dépendance à un découpage ou à un motif. Inspecte les coordonnées enregistrées des parcours plutôt que de te fier à la silhouette rendue.
Les lignes deviennent des formes ferméesConfirme que le premier opérande est une ligne ouverte et que trace: false est présent. L’exemple rejette les résultats fermés ou à plusieurs segments.
L’encre dépasse une limiteDistingue la ligne médiane de l’empreinte du contour. Cet exemple ne réduit pas la zone du rayon d’une pointe ; prépare un dégagement adapté et teste le véritable outil.
Le remplissage est clairsemé ou la surface devient trop humideAjuste à l’aide d’un échantillon contrôlé stylo/matériau. L’espacement numérique seul ne prouve ni la couverture ni le séchage.
Le fichier effectue des sauts inattendusInspecte l’ordre et les transitions stylo levé dans le logiciel destinataire. Garde les parcours originaux avant d’appliquer un optimiseur, puis revérifie les trous et les limites.

Si la source n’est qu’une silhouette matricielle en aplats, le processus clipart vers SVG de PerfectVector peut aider à récupérer d’abord une illustration fermée. Inspecte le contour et chaque ouverture, puis génère les parcours de hachure à partir de la géométrie acceptée. Une illustration vectorielle propre existante peut passer directement à la préparation des hachures.

Vectoriser la silhouette et générer les parcours de hachure sont des étapes distinctes. L’emblème original de cet article a été créé en géométrie vectorielle ; ce n’est pas un exemple de conversion PerfectVector.

Questions fréquentes

Un fond SVG devient-il automatiquement des hachures de stylo ? Non. Un fond décrit une apparence peinte. Un processus de tracé a besoin de parcours de hachure ou d’une autre méthode prise en charge pour créer des traits de dessin à partir de cette zone.

Pourquoi mon aperçu de hachures contient-il encore des lignes à travers le trou ? Un découpage peut masquer des parties de lignes entières sans découper leur géométrie enregistrée. Vérifie les coordonnées enregistrées et intègre le trou à la véritable zone avant de produire des parcours explicites.

Quel espacement de hachure utiliser ? Choisis-le par un essai de stylo et de matériau à l’échelle prévue. Cet exemple numérique utilise des espacements de 5 mm et 2.5 mm pour rendre les parcours visibles ; il n’établit ni couverture uniforme ni réglage universel de stylo.

Est-ce un seul chemin continu ? Non. Le résultat grossier contient 14 parcours ouverts distincts. Son trou divise quatre rangées, et le processus de dessin doit gérer les intervalles entre parcours sans ajouter de marques indésirables.

Sources

  1. Intersection PathItem de Paper.js — documente le découpage des chemins ouverts avec trace false.
  2. Motifs SVG — définit la peinture répétée séparément de la géométrie explicite des parcours.
  3. Masquage CSS et chemins de découpe — explique pourquoi le découpage change le rendu sans découper la forme sous-jacente.
  4. Remplissage par hachures de GRBL-Plotter — montre la différence entre objets superposés et trou dans un seul chemin.
  5. Publication de Paper.js 0.12.18 — identifie la version de bibliothèque utilisée pour l’exemple exécutable.

Si une silhouette uniquement matricielle a besoin de limites modifiables, prépare un SVG candidat de clipart, inspecte ses trous et génère puis vérifie les parcours de hachure séparément avant de tester le travail physique.

Plus depuis le blog

PerfectVector

Préparez des tracés SVG nets pour
la découpe et la production

Sans carte bancaire