packages feed

mandulia-0.4: config/transition.lua

--[[--

Mandulia -- Mandelbrot/Julia explorer
Copyright (C) 2010  Claude Heiland-Allen <claudiusmaximus@goto10.org>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

--]]--

do

  --[[--

  SYNOPSIS

    s, f = transition(a, b, p, q)

  INPUTS

    a ~ {x,y,z}  z >= 0     :  source viewpoint coordinates
    b ~ {x,y,z}  z >= 0     :  target viewpoint coordinates
    p > 0                   :  zoom vs move weighting (smaller => zoomier)
    q > 1                   :  zoom factor

  OUTPUTS

    s >= 0                  :  source->target path length
    f(t) ~ {x,y,z}  z >= 0  :  viewpoint interpolator
      where 0 <= t <= s

  EXAMPLE

    s, f = transition(a, b, p, q)
    for t = 0,s,dt do
      view = f(t)
    end

  --]]--
  function transition(aa, bb, pp, qq)

    -- copy arguments
    local a = { x = aa.x, y = aa.y, z = aa.z }
    local b = { x = bb.x, y = bb.y, z = bb.z }
    local p = pp
    local q = qq

    -- transform coordinates
    local logp = math.log(p)
    local logq = math.log(q)
    local function y(z) return p * (q ^ (-z)) end
    local function z(y)
      if y > 0 then
        return math.max((logp - math.log(y)) / logq, 0)
      else
        return 0
      end
    end

    -- initial coordinates
    local dx = b.x - a.x
    local dy = b.y - a.y
    -- local x0 = 0
    local y0 = y(a.z)
    local x1 = math.sqrt(dx * dx + dy * dy)
    local y1 = y(b.z)

    if x1 > 0 then   -- circular arc centered on x-axis

      local xc = (x1*x1 + y1*y1 - y0*y0) / (2 * x1)
      local a0 = math.atan2(y0,    - xc)
      local a1 = math.atan2(y1, x1 - xc)
      local r  = math.sqrt(xc*xc + y0*y0)
      local s  = r * math.abs(a1 - a0)
      local da = (a1 - a0) / s
      local fx = dx / x1
      local fy = dy / x1
      return s, function(t)
        local at = a0 + t * da
        local dr = xc + r * math.cos(at)
        return { x = a.x + fx * dr
               , y = a.y + fy * dr
               , z = z(r * math.sin(at))
               }
      end

    else
      local s = math.abs(y1 - y0)
      if s > 0 then  -- vertical line segment

        local ds = (y1 - y0) / s
        return s, function(t)
          return { x = a.x, y = a.y, z = z(y0 + t * ds) }
        end

      else           -- end points are identical

        return 0, function(t)
          return { x = a.x, y = a.y, z = a.z }
        end

      end
    end
  end

end