GcodeViewer: React-компонент для візуалізації G-коду 3D-принтера | Robinhood Tools

GcodeViewer: React-компонент для візуалізації G-коду 3D-принтера

GcodeViewer - це простий React-компонент, розроблений для відображення та взаємодії з файлами G-коду 3D-принтера. Він забезпечує візуальне представлення шляху друку, дозволяючи користувачам перевіряти та аналізувати G-код перед друком.

Демо: GcodeViewer

Ключові особливості

  1. 3D-візуалізація: Відображає G-код у 3D-просторі, включаючи друкарський стіл та сітку.
  2. Інтерактивне керування: Масштабування, панорамування та обертання виду.
  3. Покрокове відображення шарів: Показує шлях друку різними кольорами для переміщень та екструзії.
  4. Оптимізація продуктивності: Ефективне відображення великих файлів 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]);
Пояснення ключових моментів:
  1. Колір для переміщення (travel): g-code-travel-moves.png

    • Коли рух є переміщенням без екструзії (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]);
    
  2. Тінь та екструзія: g-code-shadows.png

    • Для рухів з екструзією спочатку малюється тінь:
      • Тінь зміщена вниз на 0.05 одиниць.
      • Використовується темно-синій колір з прозорістю.
    • Потім малюється основна лінія екструзії:
      • Використовується яскраво-синій колір.
      • Товщина лінії залежить від ширини сопла та масштабу.
    // Тінь
    ctx.strokeStyle = 'rgba(0, 0, 100, 0.5)';
    // Основна лінія екструзії
    ctx.strokeStyle = 'rgba(0, 0, 255, 0.8)';
    ctx.lineWidth = (NOZZLE_WIDTH * scale) / 10;
    
  3. Маленьке коло в кінцевій точці для всіх рухів: g-code-circle.png

    • Після кожного руху (переміщення чи екструзії) малюється маленьке біле коло.
    • Розмір кола залежить від ширини сопла та масштабу.
    • Це допомагає візуалізувати окремі точки шляху.
    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. Візуалізація друкарського столу g-code-bed.png

Функція 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: Чи панорамує користувач в даний момент

Міркування щодо продуктивності

  1. Використання useCallback: Ключові функції мемоізуються для запобігання непотрібних перерендерів.
  2. Ефективне відображення на полотні: Перемальовує тільки за необхідності (при зміні стану).
  3. Оптимізований парсинг G-коду: Обробляє G-код порядково без створення великих проміжних структур даних.

API та пропси

Компонент GcodeViewer приймає один проп:

  • file: Об'єкт File, що містить G-код для візуалізації.

Висновок

Компонент GcodeViewer демонструє техніки розробки React та маніпуляцій з полотном. Він надає простий інструмент для ентузіастів 3D-друку для візуалізації та валідації їхнього G-коду перед друком, потенційно заощаджуючи час та ресурси в процесі 3D-друку.

Читайте також