-- Copyright 2013 by Till Tantau
--
-- This file may be distributed an/or modified
--
-- 1. under the LaTeX Project Public License and/or
-- 2. under the GNU Public License
--
-- See the file doc/generic/pgf/licenses/LICENSE for more information

-- @release $Header$



---
-- This library offers a number of methods for working with Bezi\'er
-- curves.

local Bezier = {}

-- Namespace
require("pgf.gd.lib").Bezier = Bezier


-- Imports

local Coordinate = require 'pgf.gd.model.Coordinate'


---
-- Compute a point ``along a curve at a time''. You provide the four
-- coordinates of the curve and a time. You get a point on the curve
-- as return value as well as the two support vector for curve
-- before this point and two support vectors for the curve after the
-- point.
--
-- For speed reasons and in order to avoid superfluous creation of
-- lots of tables, all values are provided and returned as pairs of
-- values rather than as |Coordinate| objects.
--
-- @param ax The coordinate where the curve starts.
-- @param ay
-- @param bx The first support point.
-- @param by
-- @param cx The second support point.
-- @param cy
-- @param dx The coordinate where the curve ends.
-- @param dy
-- @param t A time (a number).
--
-- @return The point |p| on the curve at time |t| ($x$-part).
-- @return The point |p| on the curve at time |t| ($y$-part).
-- @return The first support point of the curve between |a| and |p| ($x$-part).
-- @return The first support point of the curve between |a| and |p| ($y$-part).
-- @return The second support point of the curve between |a| and |p| ($x$-part).
-- @return The second support point of the curve between |a| and |p| ($y$-part).
-- @return The first support point of the curve between |p| and |d| ($x$-part).
-- @return The first support point of the curve between |p| and |d| ($y$-part).
-- @return The second support point of the curve between |p| and |d| ($x$-part).
-- @return The second support point of the curve between |p| and |d| ($y$-part).

function Bezier.atTime(ax,ay,bx,by,cx,cy,dx,dy,t)

  local s = 1-t

  local ex, ey = ax*s + bx*t, ay*s + by*t
  local fx, fy = bx*s + cx*t, by*s + cy*t
  local gx, gy = cx*s + dx*t, cy*s + dy*t

  local hx, hy = ex*s + fx*t, ey*s + fy*t
  local ix, iy = fx*s + gx*t, fy*s + gy*t

  local jx, jy = hx*s + ix*t, hy*s + iy*t

  return jx, jy, ex, ey, hx, hy, ix, iy, gx, gy
end


---
-- The ``coordinate version'' of the |atTime| function, where both the
-- parameters and the return values are coordinate objects.

function Bezier.atTimeCoordinates(a,b,c,d,t)
  local jx, jy, ex, ey, hx, hy, ix, iy, gx, gy =
    Bezier.atTime(a.x,a.y,b.x,b.y,c.x,c.y,d.x,d.y,t)

  return
    Coordinate.new(jx, jy),
    Coordinate.new(ex, ey),
    Coordinate.new(hx, hy),
    Coordinate.new(ix, iy),
    Coordinate.new(gx, gy)
end


---
-- Computes the support points of a Bezier curve based on two points
-- on the curves at certain times.
--
-- @param from The start point of the curve
-- @param p1 A first point on the curve
-- @param t1 A time when this point should be reached
-- @param p2 A second point of the curve
-- @param t2 A time when this second point should be reached
-- @param to The end of the curve
--
-- @return sup1 A first support point of the curve
-- @return sup2 A second support point of the curve

function Bezier.supportsForPointsAtTime(from, p1, t1, p2, t2, to)

  local s1 = 1 - t1
  local s2 = 1 - t2

  local f1a = s1^3
  local f1b = t1 * s1^2 * 3
  local f1c = t1^2 * s1 * 3
  local f1d = t1^3

  local f2a = s2^3
  local f2b = t2 * s2^2 * 3
  local f2c = t2^2 * s2 * 3
  local f2d = t2^3

  -- The system:
  -- p1.x - from.x * f1a - to.x * f1d = sup1.x * f1b + sup2.x * f1c
  -- p2.x - from.x * f2a - to.x * f2d = sup1.x * f2b + sup2.x * f2c
  --
  -- p1.y - from.y * f1a - to.y * f1d = sup1.y * f1b + sup2.y * f1c
  -- p2.y - from.y * f2a - to.y * f2d = sup1.y * f2b + sup2.y * f2c

  local a = f1b
  local b = f1c
  local c = p1.x - from.x * f1a - to.x * f1d
  local d = f2b
  local e = f2c
  local f = p2.x - from.x * f2a - to.x * f2d

  local det = a*e - b*d
  local x1 = -(b*f - e*c)/det
  local x2 = -(c*d - a*f)/det

  local c = p1.y - from.y * f1a - to.y * f1d
  local f = p2.y - from.y * f2a - to.y * f2d

  local det = a*e - b*d
  local y1 = -(b*f - e*c)/det
  local y2 = -(c*d - a*f)/det

  return Coordinate.new(x1,y1), Coordinate.new(x2,y2)

end






-- Done

return Bezier