GcodeViewer: A React Component for Visualizing 3D Printer G-code | Robinhood Tools

GcodeViewer: A React Component for Visualizing 3D Printer G-code

The GcodeViewer is a simple React component designed to render and interact with 3D printer G-code files. It provides a visual representation of the printing path, allowing users to inspect and analyze the G-code before actual printing.

Demo: GcodeViewer

Key Features

  1. 3D Visualization: Renders G-code in a 3D space, including the print bed and grid.
  2. Interactive Controls: Zoom, pan, and rotate the view.
  3. Layer-by-layer Rendering: Displays the print path with different colors for travel and extrusion moves.
  4. Performance Optimization: Efficient rendering of large G-code files.

Technical Overview

Core Technologies

  • React: For building the user interface and managing component state.
  • HTML5 Canvas: For rendering the 3D visualization.
  • Styled-components: For component styling.

Key Functions and Principles

1. G-code Parsing and Rendering

The drawGcode function is the core of the visualization process. It iterates through the G-code lines, interpreting commands, and rendering them on the canvas.

This function:

  • Parses G-code commands (G0, G1)
  • Tracks the current position (X, Y, Z)
  • Distinguishes between travel moves and extrusion
  • Applies 3D transformations (rotation, scaling)
  • Renders the path with appropriate styles
Detailed Analysis of the drawGcode Function
const drawGcode = useCallback(() => {
    // ... (initial part of the function)

    gcode.split('\n').forEach(line => {
        const parts = line.split(' ');
        if (parts[0] === 'G0' || parts[0] === 'G1') {
            // ... (parsing coordinates)

            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) {
                // Color for non-extrusion movement (travel)
                ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
                ctx.lineWidth = 0.5 / scale;
                ctx.setLineDash([1 / scale, 1 / scale]);
            } else {
                // Color and shadow for extrusion
                const shadowOffset = 0.05;
                const shadowStart = rotatePoint(prevX, prevY, prevZ - shadowOffset, rotation);
                const shadowEnd = rotatePoint(x, y, z - shadowOffset, rotation);

                // Drawing the shadow
                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();

                // Drawing the main extrusion line
                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();

            // Drawing a small circle at the endpoint for all movements
            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();
        }
    });

    // ... (final part of the function)
}, [gcode, scale, offset, rotation, drawBedAndGrid]);
Explanation of Key Points:
  1. Color for Travel Movement: g-code-travel-moves.png

    • When the movement is non-extrusion (G0 or !isExtrusion), a yellow color with transparency is used.
    • The line is made thinner (0.5 / scale) and dashed ([1 / scale, 1 / scale]).
    ctx.strokeStyle = 'rgba(255, 255, 0, 0.8)';
    ctx.lineWidth = 0.5 / scale;
    ctx.setLineDash([1 / scale, 1 / scale]);
    
  2. Shadow and Extrusion: g-code-shadows.png

    • For extrusion movements, a shadow is drawn first:
      • The shadow is offset downwards by 0.05 units.
      • A dark blue color with transparency is used.
    • Then the main extrusion line is drawn:
      • A bright blue color is used.
      • The line thickness depends on the nozzle width and scale.
    // Shadow
    ctx.strokeStyle = 'rgba(0, 0, 100, 0.5)';
    // Main extrusion line
    ctx.strokeStyle = 'rgba(0, 0, 255, 0.8)';
    ctx.lineWidth = (NOZZLE_WIDTH * scale) / 10;
    
  3. Small Circle at the Endpoint for All Movements: g-code-circle.png

    • After each movement (travel or extrusion), a small white circle is drawn.
    • The circle size depends on the nozzle width and scale.
    • This helps visualize individual points along the path.
    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();
    

These implementation details provide a clear visual distinction between different types of movements in the G-code, add depth to the visualization through shadows, and provide additional information about the tool path through small circles at each point. This allows users to easily interpret the visualized G-code and understand the print path.

2. 3D Transformations

The rotatePoint function applies 3D rotations to points:

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 };
};

This function applies rotations around the X, Y, and Z axes using matrix transformations.

3. Print Bed Visualization g-code-bed.png

The drawBedAndGrid function renders the print bed and grid:

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]);

It draws:

  • The print bed surface
  • Side faces of the bed
  • A grid for reference
  • X, Y, and Z axes

4. User Interactions

Several functions handle user interactions:

  • handleWheel: Zoom in/out
  • handleMouseDown, handleMouseMove, handleMouseUp: Pan the view
  • handleRotate: Rotate the view around X, Y, or Z axis

These functions update the component's state (scale, offset, rotation), which triggers a re-render of the visualization.

State Management

The component uses React's useState hook to manage several pieces of state:

  • gcode: The parsed G-code content
  • scale: Current zoom level
  • offset: Pan position
  • rotation: Current 3D rotation
  • isDragging: Whether the user is currently panning

Performance Considerations

  1. Use of useCallback: Key functions are memoized to prevent unnecessary re-renders.
  2. Efficient canvas rendering: Only redraws when necessary (on state changes).
  3. Optimized G-code parsing: Processes G-code line by line without creating large intermediate data structures.

API and Props

The GcodeViewer component accepts a single prop:

  • file: A File object containing the G-code to be visualized.

Conclusion

The GcodeViewer component demonstrates advanced techniques in React development and canvas manipulation. It provides a powerful tool for 3D printing enthusiasts to visualize and validate their G-code before printing, potentially saving time and resources in the 3D printing process.

Read Also