
class GeoElement
{
  topo=null
  priority=100

  constructor(t, params)
  {
    topo=t

    if (params) foreach (n,v in params)
      if (n in this) this[n]=v
  }

  function startGen() {}
  function applyMinMax(minmap, maxmap) {}
  function beforeErosion() {}
  function duringErosion() {}
  function afterErosion() {}
}


class GeoPlacedElement extends GeoElement
{
  ox=0
  oz=0

  function posX() { return topo.ox+ox }
  function posZ() { return topo.oz+oz }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class GeoCircle extends GeoPlacedElement
{
  rad=0
  avgh=0
  rndh=2
  blendMin=-0.3
  blendMax=0

  avgArea=true
  avgBlend=0.5

  constructor(t, r, params=null)
  {
    rad=r
    base.constructor(t, params)
  }

  function applyMinMax(minmap, maxmap)
  {
    local cx=posX(), cz=posZ()
    local r=rad
    local mr=r*(1-(blendMin<blendMax?blendMin:blendMax))
    local mr2=mr*mr

    local ah=avgh
    if (avgArea)
    {
      local sumw=0.0, sumh=0.0

      minmap.processRect(cx-mr, cz-mr, cx+mr, cz+mr, function(x, z, v)
      {
        local dx=wrap(x-cx)
        local dz=wrap(z-cz)
        local d2=dx*dx+dz*dz
        if (d2>mr2) return v;
        local f=smoothstep(blendMin, blendMax, 1-sqrt(d2)/r)
        sumw+=f
        sumh+=((maxmap.getraw(x, z)-v)*avgBlend+v)*f
        return v;
      }.bindenv(this))

      ah=sumh/sumw+avgh
    }

    local minh=ah-rndh, maxh=ah+rndh

    minmap.processRect(cx-mr, cz-mr, cx+mr, cz+mr, function(x, z, v)
    {
      local dx=wrap(x-cx)
      local dz=wrap(z-cz)
      local d2=dx*dx+dz*dz
      if (d2>mr2) return v;
      local f=smoothstep(blendMin, blendMax, 1-sqrt(d2)/r)
      return (minh-v)*f+v
    }.bindenv(this))

    maxmap.processRect(cx-mr, cz-mr, cx+mr, cz+mr, function(x, z, v)
    {
      local dx=wrap(x-cx)
      local dz=wrap(z-cz)
      local d2=dx*dx+dz*dz
      if (d2>mr2) return v;
      local f=smoothstep(blendMin, blendMax, 1-sqrt(d2)/r)
      return (maxh-v)*f+v
    }.bindenv(this))
  }
}


//ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ//


class GeoSkeleton extends GeoElement
{
  skelmap=null

  maskName="mask"
  randomness=0.5
  blendMin=-0.3
  blendMax=0
  blendFunc=null
  avgh=0
  rndh=2
  minFunc=null
  maxFunc=null

  constructor(t, params=null)
  {
    base.constructor(t, params)
  }

  function startGen()
  {
    skelmap=make_skeleton_map(topo[maskName].shiftedCopy(topo.ox, topo.oz), randomness)
  }

  function defaultBlend(s, m) { return smoothstep(blendMin, blendMax, s/m); }

  function applyMinMax(minmap, maxmap)
  {
    local smap=skelmap
    local smax=smap.calcMinMax()[1]

    local bfunc=blendFunc?blendFunc:defaultBlend.bindenv(this)

    local hfunc = minFunc ? minFunc : (@(s, m) avgh-rndh).bindenv(this)

    minmap.processAll(function(x, z, v)
    {
      local s=smap.getraw(x, z)
      local f=bfunc(s, smax)
      if (f<=0) return v;
      local h=hfunc(s, smax)
      return (h-v)*f+v;
    })

    local hfunc = maxFunc ? maxFunc : (@(s, m) avgh+rndh).bindenv(this)

    maxmap.processAll(function(x, z, v)
    {
      local s=smap.getraw(x, z)
      local f=bfunc(s, smax)
      if (f<=0) return v;
      local h=hfunc(s, smax)
      return (h-v)*f+v;
    })
  }
}
