GcodeViewer: React-компонент для візуалізації G-коду 3D-принтера
GcodeViewer - це простий React-компонент, розроблений для відображення та взаємодії з файлами G-коду 3D-принтера. Він забезпечує візуальне представлення шляху друку, дозволяючи користувачам перевіряти та аналізувати G-код перед друком.
Демо: GcodeViewer
Ключові особливості
- 3D-візуалізація: Відображає G-код у 3D-просторі, включаючи друкарський стіл та сітку.
- Інтерактивне керування: Масштабування, панорамування та обертання виду.
- Покрокове відображення шарів: Показує шлях друку різними кольорами для переміщень та екструзії.
- Оптимізація продуктивності: Ефективне відображення великих файлів G-коду.
Технічний огляд
Основні технології
- React: Для побудови користувацького інтерфейсу та керування станом компонента.
- HTML5 Canvas: Для відображення 3D-візуалізації.
- Styled-components: Для стилізації компонентів.
Ключові функції та принципи
1. Парсинг та відображення G-коду
Функція drawGcode є ядром процесу візуалізації. Вона ітерує через рядки G-коду, інтерпретує команди та відображає їх на полотні.
Ця функція:
- Парсить команди G-коду (G0, G1)
- Відстежує поточну позицію (X, Y, Z)
- Розрізняє переміщення та екструзію
- Застосовує 3D-трансформації (обертання, масштабування)
- Відображає шлях з відповідними стилями
Детальний аналіз функції drawGcode
const drawGcode = useCallback(() => {
// ... (початкова частина функції)
gcode.split('\n').forEach(line => {
const parts = line.split(' ');
if (parts[0] === 'G0' || parts[0] === 'G1') {
// ... (парсинг координат)
const start = rotatePoint(prevX, prevY, prevZ, rotation);
const end = rotatePoint(x, y, z, rotation);
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
if (parts[0] === 'G0' || !isExtrusion) {
// Колір для переміщення без екструзії (travel)
ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
ctx.lineWidth = 0.5 / scale;
ctx.setLineDash([1 / scale, 1 / scale]);
} else {
// Колір та тінь для екструзії
const shadowOffset = 0.05;
const shadowStart = rotatePoint(prevX, prevY, prevZ - shadowOffset, rotation);
const shadowEnd = rotatePoint(x, y, z - shadowOffset, rotation);
// Малювання тіні
ctx.beginPath();
ctx.moveTo(shadowStart.x, shadowStart.y);
ctx.lineTo(shadowEnd.x, shadowEnd.y);
ctx.strokeStyle = 'rgba(0, 0, 100, 0.5)';
ctx.lineWidth = (NOZZLE_WIDTH * scale) / 10;
ctx.setLineDash([]);
ctx.stroke();
// Малювання основної лінії екструзії
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.strokeStyle = 'rgba(0, 0, 255, 0.8)';
ctx.lineWidth = (NOZZLE_WIDTH * scale) / 10;
ctx.setLineDash([]);
}
ctx.stroke();
// Малювання маленького кола в кінцевій точці для всіх рухів
ctx.beginPath();
ctx.arc(end.x, end.y, (NOZZLE_WIDTH * scale) / 16, 0, 2 * Math.PI);
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.fill();
}
});
// ... (завершальна частина функції)
}, [gcode, scale, offset, rotation, drawBedAndGrid]);
Пояснення ключових моментів:
-
Колір для переміщення (travel):
- Коли рух є переміщенням без екструзії (G0 або !isExtrusion), використовується жовтий колір з прозорістю.
- Лінія робиться тоншою (0.5 / scale) і пунктирною ([1 / scale, 1 / scale]).
ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)'; ctx.lineWidth = 0.5 / scale; ctx.setLineDash([1 / scale, 1 / scale]); -
- Для рухів з екструзією спочатку малюється тінь:
- Тінь зміщена вниз на 0.05 одиниць.
- Використовується темно-синій колір з прозорістю.
- Потім малюється основна лінія екструзії:
- Використовується яскраво-синій колір.
- Товщина лінії залежить від ширини сопла та масштабу.
// Тінь ctx.strokeStyle = 'rgba(0, 0, 100, 0.5)'; // Основна лінія екструзії ctx.strokeStyle = 'rgba(0, 0, 255, 0.8)'; ctx.lineWidth = (NOZZLE_WIDTH * scale) / 10; - Для рухів з екструзією спочатку малюється тінь:
-
Маленьке коло в кінцевій точці для всіх рухів:
- Після кожного руху (переміщення чи екструзії) малюється маленьке біле коло.
- Розмір кола залежить від ширини сопла та масштабу.
- Це допомагає візуалізувати окремі точки шляху.
ctx.beginPath(); ctx.arc(end.x, end.y, (NOZZLE_WIDTH * scale) / 16, 0, 2 * Math.PI); ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; ctx.fill();
Ці деталі реалізації забезпечують чітку візуальну відмінність між різними типами рухів у G-коді, додають глибину до візуалізації за допомогою тіней та надають додаткову інформацію про шлях інструменту через маленькі кола в кожній точці. Це дозволяє користувачам легко інтерпретувати візуалізований G-код та розуміти шлях друку.
2. 3D-трансформації
Функція rotatePoint застосовує 3D-обертання до точок:
const rotatePoint = (x, y, z, rotation = { x: 0, y: 0, z: 0 }) => {
z = z + 10; // Adjust z to account for bed height
// Rotate around Z axis
let x1 = x * Math.cos(rotation.z) - y * Math.sin(rotation.z);
let y1 = x * Math.sin(rotation.z) + y * Math.cos(rotation.z);
// Rotate around Y axis
let x2 = x1 * Math.cos(rotation.y) + z * Math.sin(rotation.y);
let z1 = -x1 * Math.sin(rotation.y) + z * Math.cos(rotation.y);
// Rotate around X axis
let y2 = y1 * Math.cos(rotation.x) - z1 * Math.sin(rotation.x);
let z2 = y1 * Math.sin(rotation.x) + z1 * Math.cos(rotation.x);
return { x: x2, y: y2, z: z2 };
};
Ця функція застосовує обертання навколо осей X, Y та Z, використовуючи матричні трансформації.
3. Візуалізація друкарського столу
Функція drawBedAndGrid відображає друкарський стіл та сітку:
const drawBedAndGrid = useCallback((ctx) => {
const { width, depth, height } = BED_DIMENSIONS;
ctx.save();
ctx.translate(offset.x, ctx.canvas.height - offset.y);
ctx.scale(scale, -scale);
// Draw bed
ctx.fillStyle = 'rgba(100, 100, 100, 0.5)';
const corners = [
rotatePoint(0, 0, 0, rotation),
rotatePoint(width, 0, 0, rotation),
rotatePoint(width, depth, 0, rotation),
rotatePoint(0, depth, 0, rotation),
rotatePoint(0, 0, -height, rotation),
rotatePoint(width, 0, -height, rotation),
rotatePoint(width, depth, -height, rotation),
rotatePoint(0, depth, -height, rotation)
];
// Draw top face
ctx.beginPath();
ctx.moveTo(corners[0].x, corners[0].y);
for (let i = 1; i < 4; i++) {
ctx.lineTo(corners[i].x, corners[i].y);
}
ctx.closePath();
ctx.fill();
// Draw side faces
ctx.fillStyle = 'rgba(80, 80, 80, 0.5)';
const sideFaces = [[0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]];
sideFaces.forEach(face => {
ctx.beginPath();
ctx.moveTo(corners[face[0]].x, corners[face[0]].y);
for (let i = 1; i < face.length; i++) {
ctx.lineTo(corners[face[i]].x, corners[face[i]].y);
}
ctx.closePath();
ctx.fill();
});
// Draw grid
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
ctx.lineWidth = 0.5 / scale;
// Draw vertical lines
for (let i = 0; i <= width; i += 10) {
let start = rotatePoint(i, 0, 0, rotation);
let end = rotatePoint(i, depth, 0, rotation);
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
}
// Draw horizontal lines
for (let i = 0; i <= depth; i += 10) {
let start = rotatePoint(0, i, 0, rotation);
let end = rotatePoint(width, i, 0, rotation);
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
ctx.stroke();
}
// Draw axes
ctx.strokeStyle = 'red';
let xAxis = rotatePoint(50, 0, 0, rotation);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(xAxis.x, xAxis.y);
ctx.stroke();
ctx.strokeStyle = 'green';
let yAxis = rotatePoint(0, 50, 0, rotation);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(yAxis.x, yAxis.y);
ctx.stroke();
ctx.strokeStyle = 'blue';
let zAxis = rotatePoint(0, 0, 50, rotation);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(zAxis.x, zAxis.y);
ctx.stroke();
ctx.restore();
}, [offset, scale, rotation]);
Вона малює:
- Поверхню друкарського столу
- Бокові грані столу
- Сітку для орієнтації
- Осі X, Y та Z
4. Взаємодія з користувачем
Декілька функцій обробляють взаємодію з користувачем:
handleWheel: Збільшення/зменшення масштабуhandleMouseDown,handleMouseMove,handleMouseUp: Панорамування видуhandleRotate: Обертання виду навколо осей X, Y або Z
Ці функції оновлюють стан компонента (масштаб, зміщення, обертання), що викликає перемалювання візуалізації.
Керування станом
Компонент використовує хук React useState для керування кількома частинами стану:
gcode: Розпарсений вміст G-кодуscale: Поточний рівень масштабуванняoffset: Позиція панорамуванняrotation: Поточне 3D-обертанняisDragging: Чи панорамує користувач в даний момент
Міркування щодо продуктивності
- Використання
useCallback: Ключові функції мемоізуються для запобігання непотрібних перерендерів. - Ефективне відображення на полотні: Перемальовує тільки за необхідності (при зміні стану).
- Оптимізований парсинг G-коду: Обробляє G-код порядково без створення великих проміжних структур даних.
API та пропси
Компонент GcodeViewer приймає один проп:
file: Об'єкт File, що містить G-код для візуалізації.
Висновок
Компонент GcodeViewer демонструє техніки розробки React та маніпуляцій з полотном. Він надає простий інструмент для ентузіастів 3D-друку для візуалізації та валідації їхнього G-коду перед друком, потенційно заощаджуючи час та ресурси в процесі 3D-друку.
