utils_svgSymbols.mjs

/**
## mapp.utils.svgSymbols{}

@module /utils/svgSymbols
*/

import { svg } from 'uhtml'

const xmlSerializer = new XMLSerializer()

const memoizedIcons = {
  dot: new Map()
}

export function dot(style) {

  const lookup = Object.values({
    fillColor: style.fillColor
  }).reduce((a, b) => a + b)
  
  if (memoizedIcons.dot.has(lookup)) return memoizedIcons.dot.get(lookup)

  let icon = svg.node`
    <svg width=24 height=24 viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
      <circle cx=13 cy=13 r=10 fill='#333'></circle>
      <circle cx=12 cy=12 r=10 fill=${style.fillColor || '#fff'}></circle>`

  icon = `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`

  memoizedIcons.dot.set(lookup, icon)

  return icon
}

export function target(style) {

  const icon = svg.node`
    <svg width=24 height=24 viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
      <circle cx=13 cy=13 fill='#333' r=10 opacity=0.4></circle>
      <circle cx=12 cy=12 r=10 fill=${style.fillColor || '#FFF'}>`

  style.layers && Object.entries(style.layers).forEach(layer => {

    icon.appendChild(svg.node`<circle cx=12 cy=12 r=${parseFloat(layer[0]) * 10} fill=${layer[1]}>`)
  })

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function triangle(style) {

  const icon = svg.node`
    <svg width=24 height=24 viewBox='${`0 0 24 24`}' xmlns='http://www.w3.org/2000/svg'>
      <polygon
        points="12,4.68 2,22 22,22" 
        fill="#333" stroke="#333" opacity=0.4 stroke-opacity=0.4 stroke-width=3
        stroke-linejoin="round"/>`

  icon.appendChild(svg.node`
    <polygon
      fill=${style.fillColor || '#FFF'} stroke=${style.fillColor || '#FFF'} stroke-width=2
      points="12,4.68 2,22 22,22"  stroke-linejoin="round"/>`)

  style.layers && Object.entries(style.layers).forEach(layer => {

    function x(val, scale) {
      return 12 + (val - 12) * scale
    }

    function y(val, scale) {
      return 16 + (val - 16) * scale
    }

    const points = `${x(12, layer[0])},${y(4.68, layer[0])} ${x(2, layer[0])}, ${y(22, layer[0])} ${x(22, layer[0])}, ${y(22, layer[0])}`

    icon.appendChild(svg.node`
      <polygon
        points="${points}"
        fill=${layer[1]}
        stroke=${layer[1] || '#FFF'}
        stroke-width=1
        stroke-linejoin="round"/>`)
  })

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function square(style) {

  const icon = svg.node`
    <svg width=24 height=24 viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
      <rect fill='#333' opacity=0.3 width=20 height=20 x=2 y=2 rx=1></rect>
      <rect fill=${style.fillColor || '#FFF'} width=20 height=20 x=0 y=0 rx=1></rect>`

  style.layers && Object.entries(style.layers).forEach(layer => {

    icon.appendChild(svg.node`
      <rect fill=${layer[1]}
        width=${parseFloat(layer[0]) * 20}
        height=${parseFloat(layer[0]) * 20}
        x=${10*(1-parseFloat(layer[0]))}
        y=${10*(1-parseFloat(layer[0]))} rx=${parseFloat(layer[0])}></rect>`)
  })

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function diamond(style) {

  const icon = svg.node`
    <svg width=24 height=24 viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
      <polygon
        fill='#333'
        opacity=0.3
        points="12 0, 24 12, 12 24, 0 12"></polygon>
      <polygon
        fill=${style.fillColor || '#FFF'}
        points="12 0, 24 12, 12 24, 0 12"></polygon>`

  function s(val, scale) {
    return 12 + (val - 12) * scale
  }

  style.layers && Object.entries(style.layers).forEach(layer => {
    
    const points = `${s(12, layer[0])} ${s(0, layer[0])},${s(24, layer[0])} ${s(12, layer[0])},${s(12, layer[0])} ${s(24, layer[0])}, ${s(0, layer[0])} ${s(12, layer[0])}`

    icon.appendChild(svg.node`
      <polygon
        fill=${layer[1] || '#FFF'}
        points="${points}"></polygon>`)
  })

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function semiCircle(style) {

  const icon = svg.node`
    <svg width=30 height=30 viewBox='0 0 20 24' xmlns='http://www.w3.org/2000/svg'>
      <defs>
        <clipPath id="cut-off-shade">
          <rect x="0" y="0" width="24" height="11"/>
        </clipPath>
        <clipPath id="cut-off">
          <rect x="0" y="0" width="24" height="10"/>
        </clipPath>
      </defs>
      <circle cx="11" cy="10" r="10" clip-path="url(#cut-off-shade)" fill="#333" opacity=0.4></circle>
      <circle cx=10 cy=10 r=10 fill=${style.fillColor || '#FFF'} clip-path="url(#cut-off-shade)">`

  style.layers && Object.entries(style.layers).forEach(layer => {
    
    icon.appendChild(svg.node`
      <circle cx=10 cy=10 r=${parseFloat(layer[0]) * 10} fill=${layer[1]} clip-path="url(#cut-off-shade)">`)

  })

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function markerLetter(style) {

  const icon = svg.node`
    <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
      <path fill="${style.color}"
        d="M 12 1.969 C 7.878 1.969 4.813 5.239 4.813 9.328 C 4.813 10.554 5.222 12.189 6.244 14.028 C 8.289 17.504 12 22 12 22 C 12 22 16.055 17.504 18.099 14.028 C 19.122 12.189 19.341 10.963 19.341 9.328 C 19.341 5.239 16.054 1.969 12 1.969 Z"/>
      <circle cx="12.17192400568182" cy="8.918683238636365" r="5.109789772727275" fill="rgb(255, 255, 255)"/>
      <text x="12" y="12" style="text-anchor: middle; font-weight: 600; font-size: 9px; font-family: sans-serif; fill: rgb(85, 85, 85);">
      ${style.letter}`

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function markerColor(style) {

  const icon = svg.node`
    <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
      <path style="opacity: 0.5;" fill="${style.colorMarker}"
        d="M 10.797 1.238 C 6.308 1.238 2.716 4.83 2.716 9.32 C 2.716 11.116 3.165 12.463 4.287 14.483 C 6.532 18.3 9.952 23.234 9.952 23.234 C 9.952 23.234 15.063 18.3 17.308 14.483 C 18.43 12.463 18.879 11.116 18.879 9.32 C 18.879 4.83 15.287 1.238 10.797 1.238 Z"/>
      <path fill="${style.colorMarker}"
        d="M 10 1.238 C 5.51 1.238 2.144 4.83 2.144 9.32 C 2.144 10.667 2.593 12.463 3.716 14.483 C 5.961 18.3 10 23.238 10 23.238 C 10 23.238 14.491 18.3 16.736 14.483 C 17.859 12.463 18.1 11.116 18.1 9.32 C 18.1 4.83 14.49 1.238 10 1.238 Z"/>
      <circle cx="10.226" cy="8.871" r="5.612" opacity="0.8" fill="rgb(255, 255, 255)"/>
      <circle cx="10.226" cy="8.871" r="2.806" opacity="0.8" fill="${style.colorDot}"/>`

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

export function circle(style) {

  const icon = svg.node`
    <svg width=24 height=24 viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'>
      <circle cx=16 cy=16 r=10
        stroke="${style.strokeColor || '#333'}"
        stroke-width="${style.strokeWidth || 1}"
        fill="#ffffff33">`

  return `data:image/svg+xml,${encodeURIComponent(xmlSerializer.serializeToString(icon))}`
}

// Assign template method to svgSymbols.
export function template(icon) {

  // Get svgString from svgSymbol.templates.
  let svgString = mapp.utils.svgSymbols.templates?.[icon.template]

  // Return undefined if svgString not found in templates.
  if (!svgString) return;

  // Iterate through the icon.substitute entries.
  if (typeof icon.substitute === 'object') {

    Object.entries(icon.substitute).forEach((entry) => {

      // Replace substitute key with values.
      svgString = svgString.replaceAll(entry[0], entry[1]);
    });
  }

  // Return encoded string.
  return `data:image/svg+xml,${encodeURIComponent(svgString)}`;
}