const {cos, sin, sqrt, acos, atan, atan2, abs, PI} = Math
const clamp = (a, b, x) => x < a ? a : x > b ? b : x
const cvs = document.getElementById("cvs")
const ctx = cvs.getContext('2d')

const NB_SECTIONS = 6
const SCALE = devicePixelRatio

let RADIUS, width, height, LINE_WIDTH

const SPHERE_SIZE = 0.15 // % of viewport width

function resize() {
  const vw = window.innerWidth * SPHERE_SIZE
  RADIUS     = Math.round((vw - 20) / 2)
  LINE_WIDTH = vw / 50  // adjust the divisor to taste
  width  = RADIUS * 2 + 20
  height = RADIUS * 2 + 20
  cvs.width  = width  * SCALE
  cvs.height = height * SCALE
  cvs.style.width  = `${SPHERE_SIZE * 100}vw`
  cvs.style.height = `${SPHERE_SIZE * 100}vw`
  ctx.setTransform(SCALE, 0, 0, SCALE, 0, 0)
  ctx.fillStyle = '#071c2dff'
  ctx.lineCap   = 'round'
}

resize()
window.addEventListener('resize', resize)

// orientation of camera
let theta, phi

const vec = (x = 0, y = 0, z = 0) => ({x, y, z})
vec.set = (o, x = 0, y = 0, z = 0) => { o.x = x; o.y = y; o.z = z; return o }
const Z = vec(0, 0, 1)

function project(o, {x, y, z}) {
  let ct = cos(theta), st = sin(theta)
  let cp = cos(phi),   sp = sin(phi)
  let a  = x * ct + y * st
  let px = y * ct - x * st
  let py = cp * z - sp * a
  let pz = cp * a + sp * z
  let tilt = -0.2
  let cr = cos(tilt), sr = sin(tilt)
  let tx = cr * px - sr * pz
  let tz = sr * px + cr * pz
  return vec.set(o, tx, py, tz)
}

const _p = vec()
function draw_section(n, o = 0) {
  let {x, y, z} = project(_p, n)
  let a  = atan2(y, x)
  let ry = sqrt(1 - o * o)
  let rx = ry * abs(z)
  let W  = sqrt(x * x + y * y)
  let sa = acos(clamp(-1, 1, o * (1 / W - W) / rx))
  let sb = z > 0 ? 2 * PI - sa : -sa
  ctx.beginPath()
  ctx.ellipse(x * o * RADIUS, y * o * RADIUS, rx * RADIUS, ry * RADIUS, a, sa, sb, z <= 0)
  ctx.stroke()
}

const _n = vec()
function draw_arcs() {
  for (let i = NB_SECTIONS; i--;) {
    let a = i / NB_SECTIONS * PI
    draw_section(vec.set(_n, cos(a), sin(a)))
  }
  for (let i = NB_SECTIONS - 1; i--;) {
    let a = (i + 1) / NB_SECTIONS * PI
    draw_section(Z, cos(a))
  }
}

ctx.fillStyle = '#071c2dff'
ctx.lineCap = 'round'

function render() {
  requestAnimationFrame(render)
  theta = performance.now() / 6000 * PI
  phi   = 1
  ctx.save()
  ctx.clearRect(0, 0, width, height)
  ctx.translate(width >> 1, height >> 1)
  ctx.scale(1, -1)
  ctx.lineWidth = LINE_WIDTH / 2
  ctx.strokeStyle = '#8bc8feff'
  ctx.scale(-1, -1)
  draw_arcs()
  ctx.scale(-1, -1)
  ctx.strokeStyle = '#8bc8feff'
  ctx.lineWidth = LINE_WIDTH + 2
  ctx.beginPath()
  ctx.arc(0, 0, RADIUS, 0, 2 * PI)
  ctx.stroke()
  ctx.lineWidth = LINE_WIDTH
  ctx.strokeStyle = '#8bc8feff'
  draw_arcs()
  ctx.restore()
}

requestAnimationFrame(render)
