-- $Date: 2004/01/25 13:55:33 $
-- $Revision: 1.4 $
-- $Author: jcrocholl $

with Messages; use Messages;
with Outlines; use Outlines;
with Lines; use Lines;
with Straights; use Straights;
with Cubics; use Cubics;
with Real_Vectors; use Real_Vectors;

package body Nocurves is

   function Distance
     (Start : in Vector;              -- Starting point.
      A, B  : access Line'Class-- Calculate distance between these.
     return Real
   is
      Result : Real := 0.0;
      Part   : Real;
   begin
      for Index in 1 .. 24 loop
         Part := Real(Index * 2 - 1) / 48.0;
         Result := Real'Max(Result, abs(
           Way_Point(Start, A, Part) -
           Way_Point(Start, B, Part)));
      end loop;
      return Result;
   end Distance;

   procedure Halve
     (Start   : in Vector;              -- Starting point.
      This    : access Line'Class-- Halve this curve.
      Curve_1 : out Line_Access;        -- Bezier curve for first half.
      Curve_2 : out Line_Access)        -- Bezier curve for second half.
   is
      A : Vector := Start;
      B : Vector := Cubic_Access(This).Control_A;
      C : Vector := Cubic_Access(This).Control_B;
      D : Vector := This.To;

      AB : Vector := (A + B) / 2.0;
      BC : Vector := (B + C) / 2.0;
      CD : Vector := (C + D) / 2.0;

      ABBC : Vector := (AB + BC) / 2.0;
      BCCD : Vector := (BC + CD) / 2.0;

      ABBCBCCD : Vector := (ABBC + BCCD) / 2.0;
   begin
      -- Debug("halve:");

      -- Original curve and two halves.
      -- Debug(To_String(A) & ' ' & To_String(B) & ' ' & To_String(C) & ' ' & To_String(D));
      -- Debug(To_String(A) & ' ' & To_String(AB) & ' ' & To_String(ABBC) & ' ' & To_String(ABBCBCCD));
      -- Debug(To_String(ABBCBCCD) & ' ' & To_String(BCCD) & ' ' & To_String(CD) & ' ' & To_String(D));

      -- Original curve and subdivision points.
      -- Debug(To_String(A) & ' ' & To_String(B) & ' ' & To_String(C) & ' ' & To_String(D));
      -- Debug(To_String(AB) & ' ' & To_String(BC) & ' ' & To_String(CD));
      -- Debug(To_String(ABBC) & ' ' & To_String(BCCD));
      -- Debug(To_String(ABBCBCCD));

      Curve_1 := Create(Control_A => AB, Control_B => ABBC, To => ABBCBCCD);
      Curve_2 := Create(Control_A => BCCD, Control_B => CD, To => D);
   end Halve;

   -- Approximate this cubic bezier curve by possibly multiple
   -- straight lines. Insert all the resulting straight lines in the
   -- given outline at the current iteration position.
   procedure Make_Straight
     (Start     : in Vector;              -- Starting point.
      This      : access Line'Class-- Straighten this curve.
      Tolerance : in Real;                -- Maximum distance to the straight lines.
      Insert    : access Outline)         -- Insert straight lines here.
   is
      use Line_Lists;
      Test    : Line_Access := Create(This.To);
      Curve_1 : Line_Access;
      Curve_2 : Line_Access;
   begin
      if Distance(Start, This, Test) <= Tolerance then
         -- Debug("accept:");
         -- Debug(To_String(Start) & ' ' & To_String(Test.To));
         Insert_Before_Current(Insert, Test);
      else
         Halve(Start, This, Curve_1, Curve_2);
         Make_Straight(Start, Curve_1, Tolerance, Insert);
         Make_Straight(Curve_1.To, Curve_2, Tolerance, Insert);
      end if;
   end Make_Straight;

   -- Approximate every cubic bezier curve by possibly multiple
   -- straight lines.
   procedure Make_Straight
     (This      : access Outline-- Straighten this outline.
      Tolerance : in Real)        -- Maximum distance to the straight lines.
   is
      use Line_Lists;
      Start : Vector := Last(This).To;
      Curve : Line_Access;
   begin
      Reset(This);
      Next(This);
      -- Debug("new border");
      while not End_Of_List(This) loop
         Curve := Current(This);
         Make_Straight(Start, Curve, Tolerance, This);
         Remove_Current(This);
         Start := Curve.To;
      end loop;
   end Make_Straight;

   -- Approximate every cubic bezier curve by possibly multiple
   -- straight lines. Input parameter Tolerance specifies the maximum
   -- acceptable error between the original cubic bezier curve and the
   -- approximation.
   procedure Make_Straight
     (This      : access Glyph-- Straighten this glyph.
      Tolerance : in Real)      -- Maximum distance to the straight lines.
   is
      use Glyphs.Outline_Lists;
      Outlines : Outline_List_Access;
      Outline  : Outline_Access;
   begin
      Outlines := Get_Outlines(This);
      Reset(Outlines);
      while Next(Outlines) loop
         Outline := Current(Outlines);
         Make_Straight(Outline, Tolerance);
      end loop;
   end Make_Straight;

end Nocurves;