steveruizok / perfect-freehand

Draw perfect pressure-sensitive freehand lines.

Home Page:https://perfectfreehand.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Manually creating paths?

shendrick opened this issue · comments

I'm trying to figure out how to use this library to create lines from some simple points that I generate algorithmically. However, it doesn't seem to deal well with minimal points that aren't curves.

example:

 const strokes = getStroke(
        [
          { x: 10, y: 10, pressure: 0.5 },
          { x: 10, y: 86, pressure: 0.5 },
          { x: 62, y: 86, pressure: 0.5 },
          { x: 62, y: 10, pressure: 0.5 },
        ],
        {
          size: 4,
          thinning: 0,
          smoothing: 0,
          streamline: 0,
          simulatePressure: false,
          start: {
            cap: false,
            taper: 0,
          },
          end: {
            cap: false,
            taper: 0,
          },
        }
      );

I would expect a U shape, however what appears is a sharp V shape. 🤔

Looks like if I add a point in between each of the existing four, closer to the next point, than the previous it creates the shape I expected...

     const strokes = getStroke(
        [
          { x: 10, y: 10, pressure: 0.5 },
          { x: 10, y: 85, pressure: 0.5 },
          { x: 10, y: 86, pressure: 0.5 },
          { x: 61, y: 86, pressure: 0.5 },
          { x: 62, y: 86, pressure: 0.5 },
          { x: 62, y: 11, pressure: 0.5 },
          { x: 62, y: 10, pressure: 0.5 },
        ],
        {
          size: 4,
          thinning: 0,
          smoothing: 0,
          streamline: 0,
          simulatePressure: false,
          start: {
            cap: false,
            taper: 0,
          },
          end: {
            cap: false,
            taper: 0,
          },
        }
      );

Hey @shendrick, good question! I've also been using the algo to manually create shapes and I often need to create many points between A and B. This is because the algorithm is tuned to the types of points that are generated when drawing, which tend to bunch at the start and end of the line. I've written a helper that I've been using to generate those kinds of points. It is available in @tldraw/vec but here's a snippet you can use, too.

 function add = (A: number[], B: number[]): number[] => {
    return [A[0] + B[0], A[1] + B[1]]
  }

function sub = (A: number[], B: number[]): number[] => {
    return [A[0] - B[0], A[1] - B[1]]
  }

 function mul = (A: number[], n: number): number[] => {
    return [A[0] * n, A[1] * n]
  }

function lrp = (A: number[], B: number[], t: number): number[] => {
    return add(A, mul(sub(B, A), t))
  }

  /**
   * Get an array of points (with simulated pressure) between two points.
   * @param A The first point.
   * @param B The second point.
   * @param steps The number of points to return.
   * @param ease An easing function to apply to the simulated pressure.
   */
  static pointsBetween = (
    A: number[],
    B: number[],
    steps = 6,
    ease = (t: number) => t * t * t * t
  ): number[][] => {
    return Array.from(Array(steps)).map((t, i) =>
      [...lrp(A, B, t), (1 - ease(i / steps)) / 2])
    )
}

Supporting more normal / sparse arrays of points would mean a significant adjustment to the algorithm, so I probably won't be changing it in that way. However, if I'm able to make the algorithm perform better with fewer input points, that could help in your case as well.