Grid

Sporadic angular networks link disparate semi-uniform nodes.

Loading...
import helpers from "/scratchpad/_lib/line.asset.mjs"
import sequence from "/scratchpad/_lib/sequence.asset.mjs"
import posterize from "/scratchpad/posterize/posterize.asset.mjs"
import badge from "/scratchpad/_lib/badge.asset.mjs"
import palette from "/scratchpad/_lib/palette.asset.mjs"
import * as random from "/scratchpad/_lib/random.asset.mjs"

const dpi = window.devicePixelRatio || 1

export default (canvas) => {
  const scale = dpi

  canvas.width = canvas.offsetWidth * scale
  canvas.height = canvas.offsetHeight * scale

  const ctx = canvas.getContext("2d")
  const colors = [palette.dark]

  const options = {
    grid: random.integer(24, 40),
    iterations: random.integer(4, 8),
  }

  options.strokeWidth = options.grid / 2
  options.blurRadius = options.strokeWidth / 8 + canvas.width / 800

  const shift = random.number(options.grid)

  const ow = canvas.width - options.strokeWidth * 2 - shift
  const oh = canvas.height - options.strokeWidth * 2 - shift

  let points = helpers
    .makeGrid(ow, oh, options.grid)
    .map(({ x, y, row, col }) => ({
      row,
      col,
      x: x + random.number(shift),
      y: y + random.number(shift),
    }))

  const iteration = (index) => [
    () => {
      const search = random
        .shuffle([...points])
        .slice(0, (points.length * Math.random()) / 2)

      const find = (a) => {
        const fs = [
          search.find((b) => a.row === b.row - 1 && a.col === b.col),
          search.find((b) => a.col === b.col + 1 && a.row === b.row),
          search.find((b) => a.row === b.row + 1 && a.col === b.col),
          search.find((b) => a.col === b.col - 1 && a.row === b.row),
        ]

        return (
          fs[(0 + ~~count) % 4] ||
          fs[(1 + ~~count) % 4] ||
          fs[(2 + ~~count) % 4] ||
          fs[(3 + ~~count) % 4]
        )
      }

      let a, b
      let count = 0

      ctx.save()
      ctx.translate(options.strokeWidth, options.strokeWidth)
      ctx.beginPath()

      while (search.length) {
        a = search[0]
        b = a

        ctx.moveTo(a.x, a.y)
        ctx.lineTo(b.x + 0.0001, b.y)

        while ((b = find(b))) {
          count += random.number()

          ctx.save()
          if (b.col && random.maybe(0.25)) ctx.translate(-options.grid, 0)
          if (b.row && random.maybe(0.25)) ctx.translate(0, -options.grid)
          ctx.lineTo(b.x, b.y)
          ctx.restore()

          if (b === a) break
          search.splice(search.indexOf(b), 1)
        }

        search.splice(search.indexOf(a), 1)
      }

      ctx.restore()
    },

    () => {
      const t = 1

      ctx.lineCap = "round"
      ctx.lineJoin = "round"
      ctx.lineWidth = options.strokeWidth * t
      ctx.strokeStyle = palette.dark
      ctx.stroke()
      ctx.lineWidth = (options.strokeWidth * t) / 3
      ctx.strokeStyle = palette.canvas
      ctx.stroke()
    },

    () => {
      posterize({
        ctx,
        colors,
        radius: options.blurRadius,
        ramp: options.blurRadius * 2,
      })
    },
  ]

  const steps = Array.from({ length: options.iterations }, (e, i) =>
    iteration(i)
  )

  return sequence([...steps.flat(), () => badge(ctx, { colors })])
}