Skip to content
Snippets Groups Projects
erasure.js 4.05 KiB
Newer Older
  • Learn to ignore specific revisions
  • function sqr(x) {
    
      return x ** 2
    
    function hypotenuseSquared(a, b) {
      return sqr(a) + sqr(b)
    }
    
    function distanceSquared([x0, y0], [x1, y1]) {
      return hypotenuseSquared(x0 - x1, y0 - y1)
    
    function distance(point0, point1) {
      return Math.sqrt(distanceSquared(point0, point1))
    }
    
    
    function cap01(x) {
      return Math.max(0, Math.min(1, x))
    }
    
    function distToSegmentSquared(lineStart, lineEnd, point) {
    
      const l2 = distanceSquared(lineStart, lineEnd)
    
      if (l2 === 0) return distanceSquared(point, lineStart)
    
      let t =
    
        ((point[0] - lineStart[0]) * (lineEnd[0] - lineStart[0]) +
          (point[1] - lineStart[1]) * (lineEnd[1] - lineStart[1])) /
        l2
    
      t = cap01(t)
    
      return distanceSquared(point, [
        lineStart[0] + t * (lineEnd[0] - lineStart[0]),
        lineStart[1] + t * (lineEnd[1] - lineStart[1]),
      ])
    
    function interpolate([x0, y0], [x1, y1], t) {
      return [x0 + (x1 - x0) * t, y0 + (y1 - y0) * 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 erasureInterval(lineStart, lineEnd, erasureCenter, erasureRadius) {
    
      if (!(lineStart && erasureCenter)) return undefined
    
      if (!lineEnd) {
        const dist2ToSingularity = distanceSquared(erasureCenter, lineStart)
        return dist2ToSingularity <= erasureRadius ** 2 ? [0, 0] : undefined
      }
    
    
      const distToSegment2 = distToSegmentSquared(lineStart, lineEnd, erasureCenter)
    
      if (erasureRadius ** 2 < distToSegment2) return undefined
    
      const lineLength = distance(lineStart, lineEnd)
      if (lineLength === 0) {
    
        return distToSegment2 <= erasureRadius ** 2 ? [0, 1] : undefined
    
      const projT = project(lineStart, lineEnd, erasureCenter)
      const projectionPoint = interpolate(lineStart, lineEnd, projT)
    
      const d2 = distance(erasureCenter, projectionPoint)
      const halfLength = Math.sqrt(Math.abs(sqr(erasureRadius) - sqr(d2)))
      if (halfLength === 0) return undefined
    
      let touchFromStartDist = distance(lineStart, projectionPoint)
      if (projT < 0) touchFromStartDist = -touchFromStartDist
    
      const touchBeginFromStarDist = touchFromStartDist - halfLength
      const touchEndFromStarDist = touchFromStartDist + halfLength
    
      return [
    
        cap01(touchBeginFromStarDist / lineLength),
        cap01(touchEndFromStarDist / lineLength),
    
    Nayeem Rahman's avatar
    Nayeem Rahman committed
    export function computeErasureIntervals(points, erasureCenter, erasureRadius) {
    
      return points
        .map((point, i) => ({ point, i }))
    
        .reduce((acc, { point, i }) => {
          const interval = erasureInterval(
    
            point,
            points[i + 1],
            erasureCenter,
            erasureRadius,
    
          )
          if (!interval) return acc
          return {
    
            [i]: [interval],
          }
        }, {})
    
    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)
    
      const 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
    }
    
    
    Nayeem Rahman's avatar
    Nayeem Rahman committed
    export 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 }
    }
    
    
    export function spreadErasureIntervals(intervals) {
    
    
      intervals.forEach(([l, u]) => {
        if (u > l) {
          for (let li = Math.floor(l); li < u; li++) {
            const list = spread[li] || []
    
            const [il, iu] = [Math.max(li, l), Math.min(li + 1, u)]
    
            list.push([il % 1, iu == li + 1 ? 1 : iu % 1])
    
            spread[li] = list
          }
        } else {
          spread[Math.floor(l)] = [[l % 1, u % 1]]
        }
      })
    
      return spread
    }
    
    export function flattenErasureIntervals(intervals) {
      const flatten = []
    
      for (const idx in intervals) {
        if (intervals[idx])
          flatten.push(
            intervals[idx].map(([l, u]) => [parseInt(idx) + l, parseInt(idx) + u]),
          )
      }
    
      return flatten.flat()
    }