เติมแฮตช์ SVG: สร้างพาธจริงสำหรับพล็อตเตอร์ปากกา
สร้างพาธแฮตช์เปิดจากพื้นที่ SVG ปิด ทำซ้ำการตรวจช่อง ระยะห่าง มุม และไฟล์บันทึกด้วยตัวอย่าง Paper.js ต้นฉบับ
ในหน้านี้
หากต้องการเติมแฮตช์ SVG สำหรับพล็อตเตอร์ปากกา ให้สร้างเส้นเปิดในพื้นที่ปิดที่ต้องการ แบ่งที่ขอบ แล้วส่งออกท่อนที่เหลือเป็นรูปทรง ตรวจไฟล์ที่บันทึกว่ามีทางข้ามช่องหรืออยู่นอกรูปหรือไม่ ก่อนเตรียมงานเครื่อง
สีเติมหรือภาพตัวอย่างลายเส้นไม่ได้ให้ทางวาดเหล่านั้นโดยตัวมันเอง บทเรียนนี้สร้างตราห้าเหลี่ยมต้นฉบับที่มีช่องสี่เหลี่ยม ใช้ Paper.js ตัดเส้นเปิดกับพื้นที่ แล้วเปิด SVG ที่บันทึกเพื่อตรวจ ยืนยันรูปทรงเส้นกลางดิจิทัล ไม่ใช่การพล็อตจริงที่สำเร็จ
1. เลือกพื้นที่ที่จะเติม ไม่ใช่เส้นที่จะกู้
ต้องมีบริเวณปิดที่ช่องเป็นส่วนของรูปทรง สี่เหลี่ยมขาววางเหนือรูปทึบอาจดูเป็นรู แต่พื้นที่ล่างยังอยู่ครบ เอกสารแฮตช์ฟิลล์ของ GRBL-Plotter แสดงความต่างระหว่างวัตถุซ้อนกับช่องในพาธเดียว เราไม่ได้รัน GRBL-Plotter ที่นี่
หากต้องกู้เส้นเดียวตรงกลางเส้นราสเตอร์หนา ใช้คู่มือ SVG เส้นเดียว แฮตช์สร้างหลายเส้นพาดพื้นที่ ไม่ได้กู้เส้นกลางปากกาเดิม
เก็บสำเนาภาพเติมสี ตัวอย่างตั้งใจสร้างทางแฮตช์โดยไม่เพิ่มการลากขอบ หากอยากวาดขอบนอกหรือขอบช่องด้วย ให้เตรียมเป็นทางแยกอย่างตั้งใจ
2. แยกภาพที่คลิปออกจากพาธที่ตัดจริง
แพตเทิร์น SVG ระบายแผ่นซ้ำ พาธคลิป จำกัดบริเวณแสดงโดยไม่เปลี่ยนรูปทรงต้นฉบับ ทั้งสองช่วยทำภาพบนจอ แต่แอปยังต้องมีวิธีเปลี่ยนรูปลักษณ์นั้นเป็นทางปากกา
ชุดควบคุมคลิปอย่างเดียวเก็บเส้นแนวนอนสิบเส้น เส้นละ 200 หน่วยผู้ใช้ คลิปซ่อนส่วนนอกและช่องสี่เหลี่ยม ความยาวเส้นที่เก็บยังรวม 2,000 หน่วย
ไฟล์ตัดจริงระยะห่างมากมีพาธเปิดแยก 14 เส้น รวม 1,320 หน่วย ไม่มีพาธคลิป แพตเทิร์น มาสก์ ราสเตอร์ฝัง หรือสัญลักษณ์นำกลับมาใช้ แฮตช์สี่แถวแบ่งเป็นสองพาธรอบช่อง ความต่างตรวจใน SVG ที่บันทึกได้แม้ภาพตัวอย่างเหมือนกัน

คู่มือหน่วยแพตเทิร์น SVG อธิบายสีซ้ำเมื่อเป็นผลลัพธ์ที่ต้องการ แต่ที่นี่ต้องการรูปทรงเปิดชัดเจน
3. รันตัวอย่าง Paper.js ครบชุด
ใช้เบราว์เซอร์ โปรแกรมข้อความ และ Paper.js 0.12.18 แบบเต็มจากรุ่นอย่างเป็นทางการ ใส่ paper-full.min.js ในโฟลเดอร์ library บันทึก HTML นี้เป็น generate.html และ JavaScript ถัดไปเป็น hatch.js ข้างโฟลเดอร์นั้น โค้ดมีตราต้นฉบับแล้ว
<!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.';เปิด generate.html ในเบราว์เซอร์ เรารันด้วย Paper.js 0.12.18 ใน Chromium 155 แต่ละผลแสดงภาพตัวอย่างและมาร์กอัป SVG ตรงตัว คัดลอกสตริง SVG ทั้งสามครบชุดลง hatch-coarse.svg, hatch-dense.svg และ hatch-angled.svg แล้วเปิดไฟล์บันทึกเหล่านั้นแยกกัน
คำสั่งสำคัญคือ line.intersect(area, { trace: false, insert: false }) Paper.js อธิบาย trace: false ว่าแบ่งพาธแรกที่จุดตัดและเก็บส่วนโค้งที่อยู่ภายในพื้นที่พาธที่สอง จึงคงท่อนแฮตช์เปิด โหมดลากพื้นที่เริ่มต้นตอบคำถามบูลีนอีกแบบ คู่มือรูปเติมสี Paper.js อธิบายงานนั้น
ฟังก์ชันตั้ง fillColor เป็น null และให้เส้นกับแต่ละทาง ยังตรวจผลเปิดสองจุดก่อนส่งออก พื้นที่ปิดคงเป็นตัวช่วยสร้าง ไม่รวมในกลุ่มส่งมอบ
4. ตรวจระยะ มุม และสเกลจริงร่วมกัน
ทั้งสามไฟล์ระบุ width="100mm", height="70mm" และ viewBox="0 0 200 140" ทำให้หนึ่งหน่วยผู้ใช้เป็น 0.5 mm ใน SVG ดังนั้นระยะแฮตช์ 10 หน่วยคือ 5 mm ส่วน 5 หน่วยคือ 2.5 mm
ระยะตัวอย่างที่ตั้งใจให้กว้างทำให้ตรวจแต่ละทางง่าย ไม่ใช่คำแนะนำให้หมึกคลุมทึบ เส้นหน้าจอกว้างหนึ่งหน่วยผู้ใช้ แต่รูปลักษณ์ไม่ได้วัดรอยปากกาจริงบนวัสดุ
ลูป offset กำหนดเฟสแฮตช์ที่ครึ่งระยะ เปลี่ยนเฟสอาจทำให้เส้นพบขอบคนละจุด ที่บวก 30° ทิศไปขวาลงในพิกัด SVG นี้ ระยะห่างวัดตั้งฉากกับเส้นขนาน ไม่ใช่ช่องแนวตั้งที่เห็นบนจอ
| ไฟล์บันทึก | มุม | ระยะห่าง | ทางเปิด | ความยาวเส้นกลางที่วาด |
|---|---|---|---|---|
hatch-coarse.svg | 0° | 10 หน่วย / 5 mm | 14 | 1,320 หน่วย / 660 mm |
hatch-dense.svg | 0° | 5 หน่วย / 2.5 mm | 28 | 2,640 หน่วย / 1,320 mm |
hatch-angled.svg | 30° | 10 หน่วย / 5 mm | 20 | ประมาณ 1,318.564 หน่วย / 659.282 mm |
เราคำนวณจุดตัดเส้นกับรูปหลายเหลี่ยมที่คาดแยก แล้วเทียบพิกัดบันทึกด้วยค่าคลาดเคลื่อน 0.000002 หน่วยผู้ใช้ ทุกทางตรงจุดปลายที่คาด ทุกทางเปิด และไม่มีจุดตัวอย่างเส้นกลางข้ามช่องหรือด้านนอกเกินค่าคลาดเคลื่อนนั้น SVG ทั้งสามรักษาการแมปขนาดจริงที่ประกาศ
สำหรับรูปอื่น ให้ตรวจทางที่ส่งออกจริงแทนการคิดว่าจำนวนตัวอย่างนี้ใช้ต่อได้ เส้นโค้ง ช่องจิ๋ว การตัดกันเอง กลุ่มแปลงรูป และเอกสาร SVG นำเข้าใด ๆ อยู่นอกขอบเขตการทดสอบนี้
5. แยกช่องระหว่างทางจากแผนเดินที่ปรับเหมาะแล้ว
ไฟล์ระยะกว้างมี 14 ทางเปิดและ 13 ช่องระหว่างทางติดกันตามลำดับที่บันทึก การลากเชื่อมขณะปากกาลงจะเพิ่มรอยไม่ต้องการ กระบวนการพล็อตต้องจัดการการเปลี่ยนทางให้ถูกก่อนวาด
สคริปต์ใช้ลำดับทางเดียวเรียบง่าย ไล่ offset ตามลำดับและตามแต่ละท่อนไปทิศเดียวกัน ผลรวมระยะตรงจากปลายแต่ละทางถึงต้นทางถัดไปประมาณ 1,562.997 หน่วยผู้ใช้ในไฟล์ระยะกว้าง ไฟล์หนาแน่นมี 27 ช่องและผลรวมเชื่อมประมาณ 3,281.578 หน่วย
ผลรวมไม่รวมเข้าเริ่มต้นและกลับสุดท้าย เป็นผลทางเรขาคณิตจากลำดับนี้ ไม่ใช่ระยะเครื่องวัดจริง ประมาณเวลา หรือแผนปรับเหมาะ แอปพล็อตอาจกลับทิศหรือเรียงใหม่ การลดระยะครึ่งหนึ่งทำให้ความยาววาดและจำนวนทางตัวอย่างนี้เป็นสองเท่า ไม่ได้ยืนยันว่าเวลางานทุกแบบจะสองเท่า
เปิดไฟล์ในแอปควบคุมพล็อตเตอร์ ยืนยันขนาด แล้วดูภาพเส้นทางและการยกปากกา ทดสอบปากกา วัสดุ ระยะห่าง ระยะจากขอบ และค่าการเคลื่อนในเครื่องของคุณ เราไม่ได้รันพล็อตเตอร์ สร้างโค้ดเครื่อง หรือทดสอบ Cricut Draw/Attach
แก้ข้อผิดพลาดรูปทรงที่พบบ่อย
| อาการ | สิ่งที่ตรวจและเปลี่ยน |
|---|---|
| แฮตช์ข้ามช่องที่ต้องการ | ตรวจพื้นที่เติม แทนรูปขาวทับด้วยช่องจริงในรูปทรงประกอบก่อนตัดกัน |
| ภาพตัวอย่างถูกแต่ยังมีเส้นเต็ม | หา clip หรือ pattern ที่พึ่งอยู่ ตรวจพิกัดทางที่บันทึกแทนพึ่งรูปเงาที่แสดง |
| เส้นกลายเป็นรูปปิด | ยืนยันตัวตั้งแรกเป็นเส้นเปิดและมี trace: false ตัวอย่างปฏิเสธผลปิดหรือหลายท่อน |
| หมึกเลยขอบ | แยกเส้นกลางจากพื้นที่กว้างของเส้น ตัวอย่างไม่ได้หดพื้นที่ตามรัศมีหัวปากกา เตรียมระยะเว้นและทดสอบเครื่องมือจริง |
| เติมห่างหรือพื้นผิวเปียกเกิน | ปรับจากตัวอย่างปากกาและวัสดุที่ควบคุมได้ ระยะดิจิทัลอย่างเดียวไม่กำหนดการคลุมหรือแห้ง |
| ไฟล์กระโดดผิดคาด | ดูลำดับและการยกปากกาในโปรแกรมปลายทาง เก็บทางเดิมก่อนใช้ตัวปรับเหมาะ แล้วตรวจช่องและขอบใหม่ |
หากต้นทางมีแค่รูปเงาราสเตอร์สีเรียบ กระบวนการคลิปอาร์ตเป็น SVG ของ PerfectVector ช่วยกู้งานปิดก่อนได้ ตรวจขอบและทุกช่อง แล้วสร้างทางแฮตช์จากรูปทรงที่รับแล้ว เวกเตอร์สะอาดที่มีอยู่เข้าเตรียมแฮตช์ได้เลย
แปลงรูปเงาเป็นเวกเตอร์กับสร้างทางแฮตช์เป็นคนละขั้น ตราต้นฉบับบทความนี้สร้างเป็นเวกเตอร์ ไม่ใช่ตัวอย่างแปลงด้วย PerfectVector
คำถามที่พบบ่อย
สีเติม SVG กลายเป็นแฮตช์ปากกาอัตโนมัติหรือไม่? ไม่ สีเติมอธิบายรูปลักษณ์ที่ระบาย กระบวนการพล็อตต้องมีทางแฮตช์หรือวิธีที่รองรับในการสร้างเส้นวาดจากพื้นที่
ทำไมภาพแฮตช์ยังมีเส้นผ่านช่อง? คลิปซ่อนบางส่วนของเส้นเต็มได้โดยไม่ตัดรูปทรงที่เก็บ ตรวจพิกัดบันทึกและให้ช่องเป็นส่วนของพื้นที่จริงก่อนสร้างทางชัดเจน
ควรใช้ระยะแฮตช์เท่าไร? เลือกผ่านการทดสอบปากกาและวัสดุที่สเกลใช้งาน ตัวอย่างดิจิทัลนี้ใช้ 5 mm และ 2.5 mm เพื่อให้เห็นทาง ไม่ได้กำหนดการคลุมทึบหรือค่าปากกาสากล
นี่คือพาธต่อเนื่องเส้นเดียวหรือไม่? ไม่ ผลระยะกว้างมีทางเปิดแยก 14 เส้น ช่องแบ่งสี่แถว และกระบวนการวาดต้องจัดการช่องระหว่างทางโดยไม่เพิ่มรอยไม่ต้องการ
แหล่งข้อมูล
- Paper.js PathItem intersection — อธิบายการแบ่งพาธเปิดเมื่อ trace false
- แพตเทิร์น SVG — นิยามสีซ้ำแยกจากรูปทรงทางชัดเจน
- CSS masking และ clipping paths — อธิบายว่าคลิปเปลี่ยนการแสดงโดยไม่ตัดรูปต้นฉบับอย่างไร
- GRBL-Plotter hatch fill — แสดงความต่างวัตถุวางทับกับช่องในพาธเดียว
- Paper.js รุ่น 0.12.18 — ระบุรุ่นไลบรารีที่ใช้ในตัวอย่างรันได้
หากรูปเงาราสเตอร์ต้องการขอบแก้ไขได้ ให้เตรียมคลิปอาร์ต SVG ตัวเลือก ตรวจช่อง และสร้างพร้อมยืนยันทางแฮตช์แยกก่อนทดสอบงานจริง
เพิ่มเติมจากบล็อก

เส้นกรีดรอยพับกับเส้นตัด SVG: อ่านรูปทรงให้เข้าใจ
อ่านรูปทรงรอยพับ SVG ก่อนกำหนด Score หรือ Cut เปรียบเทียบพาธเปิด แถบเติมสี และเส้นประ แล้วตรวจขั้นตอนกำหนดงานของ Cricut ตามเอกสาร

สเตนซิลกัดลายแก้ว: เลือกส่วนที่จะลอกออก
เลือกพื้นที่มาสก์แก้วที่จะปกป้องและเปิดผิว ตรวจสัญลักษณ์ เกาะภายใน และรูปแบบพื้นที่ตรงข้ามตามข้อจำกัดของผู้ผลิต
