Laser Grid Design

p5-application for laser designs

Laser Grid Design

p5-application for laser designs

A laser cutting design application built with p5.js that allows users to draw lines, Bezier curves, ellipses, and save their designs as SVG files. The canvas grid and other drawing parameters can be adjusted with keyboard input, see instructions at github readme.

Notes

The size of an A4 sheet of paper in inches is 8.27 x 11.69 inches. To convert these dimensions to pixels, you need to know the DPI (dots per inch) that will be used for the conversion. For standard printing purposes, 300 DPI is typically used, but you can use other values like 72 DPI for screen displays.

Here’s the calculation for A4 size in pixels at different DPI values:

Formula:

Pixel Dimension = Inch Dimension × DPI

At 300 DPI (standard print quality):

  • Width: 8.27 inches × 300 DPI = 2,481 pixels
  • Height: 11.69 inches × 300 DPI = 3,507 pixels

So, at 300 DPI, an A4 paper would be 2481 x 3507 pixels.

At 72 DPI (standard web display):

  • Width: 8.27 inches × 72 DPI = 595 pixels
  • Height: 11.69 inches × 72 DPI = 841 pixels

Code notes

The reason for creating the saveSVGlaser function was to ensure that the SVG is saved correctly after the drawing process is fully completed. By defining a separate function, we have more control over when and how the SVG is saved. Specifically, this allows us to perform necessary actions, such as ensuring that all dynamic elements (like lines, shapes, or modifications) are fully rendered before saving, preventing incomplete or empty files from being generated. This also enables a clearer organization of the code, as the saving functionality is isolated in its own function, making the code easier to manage and modify if needed.

function saveSVGlaser() {
  // Create an SVG canvas and draw only the stored lines
  let svgCanvas = createGraphics(w, h, SVG);
  svgCanvas.noFill();
  svgCanvas.stroke(0);
  svgCanvas.strokeWeight(use_laser_strokeweight);

  for (let i = 0; i < lines.length; i++) {
    svgCanvas.line(lines[i].x1, lines[i].y1, lines[i].x2, lines[i].y2);
  }

  // Draw existing Bezier curves
  for (let bezierCurve of beziers) {
    svgCanvas.bezier(
      bezierCurve.startBx,
      bezierCurve.startBy,
      bezierCurve.controlX1,
      bezierCurve.controlY1,
      bezierCurve.controlX2,
      bezierCurve.controlY2,
      bezierCurve.endBX,
      bezierCurve.endBY
    );
  }

  // Draw all stored ellipses from the array
  for (let i = 0; i < ellipses.length; i++) {
    let e = ellipses[i];
    svgCanvas.ellipse(e.x, e.y, e.radius * 2, e.radius * 2); // Draw the ellipse
  }

  if (drawRectInSave) {
    svgCanvas.ect(0, 0, w, h);
  }

  // Save the SVG
  let datetag = new Date().toISOString().replace(/[-:]/g, "").split('.')[0];  // Generate a timestamp
  svgCanvas.save("laser_" + datetag + ".svg");
}

This function saves all the drawing elements, including lines, Bezier curves, ellipses, and an optional rectangle, to an SVG file. The file is named with a timestamp to ensure uniqueness. The function provides a clear separation between drawing and saving, making it modular and easier to manage.

Application

Bezier

  1. Dynamic Preview:
  • While selecting control points, the Bezier curve is drawn dynamically using the current mouse position for the control point being set.
  • If the first control point is being set, the mouseX and mouseY values are used for both controlX1/controlY1 and controlX2/controlY2 in the preview.
  • If the second control point is being set, mouseX and mouseY are used for controlX2/controlY2.
  1. Preview Styling:
  • The preview Bezier curve is drawn in blue (stroke(0, 0, 255)) to distinguish it from completed curves.
  1. Bezier State:
  • The bezierState variable is used to check whether the user is selecting the first or second control point. Behavior
    • After setting the start and end points:
  • When moving the mouse, you will see a blue preview of the Bezier curve using the mouse position as the control point. As you click to set each control point, the preview updates accordingly, providing real-time feedback.
 // Set control points
  if (!controlX1 && !controlY1) {
    if (snapcontrolpointstogrid) {
      controlX1 = snapToGrid(mouseX);
      controlY1 = snapToGrid(mouseY);
    } else {
      controlX1 = mouseX;
      controlY1 = mouseY;
    }
  } else {
    if (snapcontrolpointstogrid) {
      controlX2 = snapToGrid(mouseX);
      controlY2 = snapToGrid(mouseY);
    } else {
      controlX2 = mouseX;
      controlY2 = mouseY;
    }

    // Save the completed Bezier curve
    beziers.push({
      startBx,
      startBy,
      controlX1,
      controlY1,
      controlX2,
      controlY2,
      endBX,
      endBY,
    });

    resetBezierState(); // Reset for the next Bezier
  }

Equality

=== (Strict Equality) Type-sensitive: It compares both the value and the type. Returns true only if the two values are of the same type and are equal.

== (Loose Equality) Type-converting: It converts the operands to the same type before comparing them. Can return true even if the types differ, as long as the converted values are equal.

Why === is Preferred Using === avoids unexpected behavior caused by type coercion. It ensures that the comparison is intentional and predictable.

Delete objects

If beziers contains Bezier curves and not just line segments, you would need a proper distance calculation. Here’s an example approach to fix this:

  • Approximate the Bezier curve by sampling points along it (e.g., using a parameter t from 0 to 1 to sample points along the curve).
  • Calculate the distance from the mouse to each sampled point.
  • Track the minimum distance and index.

function distanceToBezierCurve(mx, my, bezierCurve) {
  let minDist = Infinity;
  for (let t = 0; t <= 1; t += 0.01) { // Sample points on the Bezier curve from t = 0 to t = 1
    let bx = bezierPoint(bezierCurve.x1, bezierCurve.controlX1, bezierCurve.controlX2, bezierCurve.x2, t);
    let by = bezierPoint(bezierCurve.y1, bezierCurve.controlY1, bezierCurve.controlY2, bezierCurve.y2, t);
    let d = dist(mx, my, bx, by);
    if (d < minDist) {
      minDist = d;
    }
  }
  return minDist;
}

function bezierPoint(p0, p1, p2, p3, t) {
  // Cubic Bezier formula: B(t) = (1-t)^3 * P0 + 3(1-t)^2 * t * P1 + 3(1-t) * t^2 * P2 + t^3 * P3
  let u = 1 - t;
  return u * u * u * p0 + 3 * u * u * t * p1 + 3 * u * t * t * p2 + t * t * t * p3;
}

let closestIndex = -1;
let minDist = Infinity;

for (let i = 0; i < beziers.length; i++) {
  let bezierCurve = beziers[i];
  let d = distanceToBezierCurve(mx, my, bezierCurve);
  if (d < minDist && d < 10) {
    minDist = d;
    closestIndex = i;
  }
}

if (closestIndex !== -1) {
  beziers.splice(closestIndex, 1); // Remove closest Bezier curve
}

Links


Disclaimer

Tags: laser p5js
Share: Twitter