Skip to content
Snippets Groups Projects
erasure.js 3.32 KiB
Newer Older
  • Learn to ignore specific revisions
  • function distance([x1, y1], [x2, y2]) {
      return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
    }
    
    function sqr(x) {
    
      return x ** 2
    
    }
    
    function dist2(v, w) {
      return sqr(v[0] - w[0]) + sqr(v[1] - w[1])
    }
    
    // p - point
    // v - start point of segment
    // w - end point of segment
    function distToSegmentSquared(p, v, w) {
      var l2 = dist2(v, w)
      if (l2 === 0) return dist2(p, v)
      var t = ((p[0] - v[0]) * (w[0] - v[0]) + (p[1] - v[1]) * (w[1] - v[1])) / l2
      t = Math.max(0, Math.min(1, t))
      return dist2(p, [v[0] + t * (w[0] - v[0]), v[1] + t * (w[1] - v[1])])
    }
    
    // p - point
    // v - start point of segment
    // w - end point of segment
    function distToSegment(p, v, w) {
      return Math.sqrt(distToSegmentSquared(p, v, w))
    }
    
    
    function distanceToLine(lineStart, lineEnd, point) {
    
      return distToSegment(point, lineStart, lineEnd)
    
    }
    
    function shouldAddErasureInterval(
      point,
      nextPoint,
      erasureCenter,
      erasureRadius,
    ) {
      if (!nextPoint) return false
      return distanceToLine(point, nextPoint, erasureCenter) <= erasureRadius
    }
    
    
    function interpolate([x1, y1], [x2, y2], t) {
      return [x1 + (x2 - x1) * t, y1 + (y2 - y1) * t]
    }
    
    function project([x1, y1], [x2, y2], [x3, y3]) {
      const x21 = x2 - x1,
        y21 = y2 - y1
      const x31 = x3 - x1,
        y31 = y3 - y1
      return (x31 * x21 + y31 * y21) / (x21 * x21 + y21 * y21)
    }
    
    function cap0(x) {
    
    Yuriy Maksymets's avatar
    Yuriy Maksymets committed
      return Math.max(0, cap1(x))
    
    }
    
    function cap1(x) {
      return Math.min(1, x)
    }
    
    
    function erasureInterval(lineStart, lineEnd, erasureCenter, erasureRadius) {
    
      const lineLength = distance(lineStart, lineEnd)
      const distToLine = distanceToLine(lineStart, lineEnd, erasureCenter)
      if (lineLength === 0) {
        return distToLine <= erasureRadius ? [0, 1] : [0, 0]
      }
    
    
      const projectionPoint = interpolate(
    
        lineStart,
        lineEnd,
        project(lineStart, lineEnd, erasureCenter),
      )
    
      const touchFromStartDist = distance(lineStart, projectionPoint)
    
      const halfLength = Math.sqrt(erasureRadius ** 2 - distToLine ** 2)
    
    
      const touchBeginFromStarDist = touchFromStartDist - halfLength
      const touchEndFromStarDist = touchFromStartDist + halfLength
    
      return [
        cap0(touchBeginFromStarDist / lineLength),
        cap1(touchEndFromStarDist / lineLength),
      ]
    
    }
    
    function computeErasureIntervals(points, erasureCenter, erasureRadius) {
      return points
        .map((point, i) => ({ point, i }))
        .filter(({ point, i }) =>
          shouldAddErasureInterval(
            point,
            points[i + 1],
            erasureCenter,
            erasureRadius,
          ),
        )
        .reduce(
          (acc, { point, i }) => ({
            ...acc,
    
            [i]: [
              erasureInterval(point, points[i + 1], erasureCenter, erasureRadius),
            ],
    
    function overlaps([s1, e1], [, e2]) {
      return s1 <= e2 && s1 <= e1
    }
    
    function mergeIntervals(...intervals) {
      if (!intervals.length) return []
    
      const sorted = intervals.sort(([a], [b]) => a > b)
      let stack = [sorted[0]]
      sorted.forEach((x) => {
        const top = stack[stack.length - 1]
        if (overlaps(x, top)) {
          if (x[1] > top[1]) top[1] = x[1]
        } else {
          stack.push(x)
        }
      })
      return stack
    }
    
    function combineErasureIntervals(i1, i2) {
      const _i1 = { ...i1 }
      Object.keys(i1).forEach((key) => {
        if (i2[key]) {
          _i1[key] = mergeIntervals(...i1[key], ...i2[key])
        }
      })
      return { ...i2, ..._i1 }
    }
    
    
    module.exports = {
      computeErasureIntervals,
    
      combineErasureIntervals,