//
// AggPas 2.4 RM3 Demo application
// Note: Press F1 key on run to see more info about this demo
//
// Paths: ..;..\ctrl;..\svg;..\util;..\platform\win;expat-wrap
//
program
 conv_stroke_ ;

uses
 agg_basics ,
 agg_platform_support ,

 agg_color ,
 agg_pixfmt ,
 agg_pixfmt_rgb ,

 agg_ctrl ,
 agg_slider_ctrl ,
 agg_rbox_ctrl ,
 agg_cbox_ctrl ,

 agg_renderer_base ,
 agg_renderer_scanline ,
 agg_rasterizer_scanline_aa ,
 agg_scanline ,
 agg_scanline_u ,
 agg_render_scanlines ,

 agg_conv_stroke ,
 agg_conv_dash ,
 agg_conv_curve ,
 agg_conv_contour ,
 agg_conv_smooth_poly1 ,
 agg_conv_marker ,
 agg_arrowhead ,
 agg_vcgen_markers_term ,
 agg_math ,
 agg_math_stroke ,
 agg_path_storage ;

{$I agg_mode.inc }

const
 flip_y = true;

type
 the_application = object(platform_support )
   m_x   ,
   m_y   : array[0..2 ] of double;
   m_dx  ,
   m_dy  : double;
   m_idx : int;

   m_join ,
   m_cap  : rbox_ctrl;

   m_width       ,
   m_miter_limit : slider_ctrl;

   constructor Construct(format_ : pix_format_e; flip_y_ : boolean );
   destructor  Destruct;

   procedure on_draw; virtual;

   procedure on_mouse_move       (x ,y : int; flags : unsigned ); virtual;
   procedure on_mouse_button_down(x ,y : int; flags : unsigned ); virtual;
   procedure on_mouse_button_up  (x ,y : int; flags : unsigned ); virtual;

   procedure on_key(x ,y : int; key ,flags : unsigned ); virtual;

  end;

{ CONSTRUCT }
constructor the_application.Construct;
begin
 inherited Construct(format_ ,flip_y_ );

 m_join.Construct       (10.0 ,10.0 ,133.0 ,80.0 ,not flip_y_ );
 m_cap.Construct        (10.0 ,80.0 + 10.0 ,133.0 ,80.0 + 80.0 ,not flip_y_ );
 m_width.Construct      (130 + 10.0 ,10.0 + 4.0 ,500.0 - 10.0 ,10.0 + 8.0 + 4.0 ,not flip_y_ );
 m_miter_limit.Construct(130 + 10.0 ,20.0 + 10.0 + 4.0 ,500.0 - 10.0 ,20.0 + 10.0 + 8.0 + 4.0 ,not flip_y_ );

 m_idx:=-1;

 m_x[0 ]:=57  + 100;
 m_y[0 ]:=60;
 m_x[1 ]:=369 + 100;
 m_y[1 ]:=170;
 m_x[2 ]:=143 + 100;
 m_y[2 ]:=310;

 add_ctrl(@m_join );

 m_join.text_size_     (7.5 );
 m_join.text_thickness_(1.0 );

 m_join.add_item ('Miter Join' );
 m_join.add_item ('Miter Join Revert' );
 m_join.add_item ('Round Join' );
 m_join.add_item ('Bevel Join' );
 m_join.cur_item_(2 );

 add_ctrl(@m_cap );

 m_cap.add_item ('Butt Cap' );
 m_cap.add_item ('Square Cap' );
 m_cap.add_item ('Round Cap' );
 m_cap.cur_item_(2 );

 add_ctrl(@m_width );

 m_width.range_(3.0 ,40.0 );
 m_width.value_(20.0 );
 m_width.label_('Width=%1.2f' );

 add_ctrl(@m_miter_limit );

 m_miter_limit.range_(1.0 ,10.0 );
 m_miter_limit.value_(4.0 );
 m_miter_limit.label_('Miter Limit=%1.2f' );

 m_join.no_transform;
 m_cap.no_transform;
 m_width.no_transform;
 m_miter_limit.no_transform;

end;

{ DESTRUCT }
destructor the_application.Destruct;
begin
 inherited Destruct;

 m_join.Destruct;
 m_cap.Destruct;
 m_width.Destruct;
 m_miter_limit.Destruct;

end;

{ ON_DRAW }
procedure the_application.on_draw;
var
 pixf : pixel_formats;
 rgba : aggclr;
 renb : renderer_base;
 ren  : renderer_scanline_aa_solid;
 ras  : rasterizer_scanline_aa;
 sl   : scanline_u8;

 cap  ,
 join : unsigned;
 path : path_storage;

 stroke ,
 poly1  ,
 poly2  : conv_stroke;

 poly2_dash : conv_dash;

begin
// Initialize structures
 pixfmt_bgr24(pixf ,rbuf_window );

 renb.Construct(@pixf );
 ren.Construct (@renb );

 rgba.ConstrDbl(1 ,1 ,1 );
 renb.clear    (@rgba );

 ras.Construct;
 sl.Construct;

// Render
 path.Construct;

 path.move_to(m_x[0 ] ,m_y[0 ] );
 path.line_to((m_x[0 ] + m_x[1 ] ) / 2 ,(m_y[0 ] + m_y[1 ] ) / 2 ); // This point is added only to check for numerical stability
 path.line_to(m_x[1 ] ,m_y[1 ] );
 path.line_to(m_x[2 ] ,m_y[2 ] );
 path.line_to(m_x[2 ] ,m_y[2 ] );                                   // This point is added only to check for numerical stability

 path.move_to((m_x[0 ] + m_x[1 ] ) / 2 ,(m_y[0 ] + m_y[1 ] ) / 2 );
 path.line_to((m_x[1 ] + m_x[2 ] ) / 2 ,(m_y[1 ] + m_y[2 ] ) / 2 );
 path.line_to((m_x[2 ] + m_x[0 ] ) / 2 ,(m_y[2 ] + m_y[0 ] ) / 2 );
 path.close_polygon;

 cap:=butt_cap;

 if m_cap._cur_item = 1 then
  cap:=square_cap;

 if m_cap._cur_item = 2 then
  cap:=round_cap;

 join:=miter_join;

 if m_join._cur_item = 1 then
  join:=miter_join_revert;

 if m_join._cur_item = 2 then
  join:=round_join;

 if m_join._cur_item = 3 then
  join:=bevel_join;

// (1)
 stroke.Construct   (@path );
 stroke.line_join_  (join );
 stroke.line_cap_   (cap );
 stroke.miter_limit_(m_miter_limit._value );
 stroke.width_      (m_width._value );

 ras.add_path    (@stroke );
 rgba.ConstrDbl  (0.8 ,0.7 ,0.6 );
 ren.color_      (@rgba );
 render_scanlines(@ras ,@sl ,@ren );

// (2)
 poly1.Construct (@path );
 poly1.width_    (1.5 );
 ras.add_path    (@poly1 );
 rgba.ConstrDbl  (0 ,0 ,0 );
 ren.color_      (@rgba );
 render_scanlines(@ras ,@sl ,@ren );

// (3)
 poly2_dash.Construct(@stroke );
 poly2.Construct     (@poly2_dash );
 poly2.miter_limit_  (4.0 );
 poly2.width_        (m_width._value / 5.0 );
 poly2.line_cap_     (cap );
 poly2.line_join_    (join );
 poly2_dash.add_dash (20.0 ,m_width._value / 2.5 );

 ras.add_path    (@poly2 );
 rgba.ConstrDbl  (0 ,0 ,0.3 );
 ren.color_      (@rgba );
 render_scanlines(@ras ,@sl ,@ren );

// (4)
 ras.add_path    (@path );
 rgba.ConstrDbl  (0.0 ,0.0 ,0.0 ,0.2 );
 ren.color_      (@rgba );
 render_scanlines(@ras ,@sl ,@ren );

// Render the controls
 render_ctrl(@ras ,@sl ,@ren ,@m_join );
 render_ctrl(@ras ,@sl ,@ren ,@m_cap );
 render_ctrl(@ras ,@sl ,@ren ,@m_width );
 render_ctrl(@ras ,@sl ,@ren ,@m_miter_limit );

// Free AGG resources
 ras.Destruct;
 sl.Destruct;

 path.Destruct;
 stroke.Destruct;
 poly1.Destruct;
 poly2_dash.Destruct;
 poly2.Destruct;

end;

{ ON_MOUSE_MOVE }
procedure the_application.on_mouse_move;
var
 dx ,dy : double;

begin
 if flags and mouse_left <> 0 then
  begin
   if m_idx = 3 then
    begin
     dx:=x - m_dx;
     dy:=y - m_dy;

     m_x[1 ]:=m_x[1 ] - (m_x[0 ] - dx );
     m_y[1 ]:=m_y[1 ] - (m_y[0 ] - dy );
     m_x[2 ]:=m_x[2 ] - (m_x[0 ] - dx );
     m_y[2 ]:=m_y[2 ] - (m_y[0 ] - dy );

     m_x[0 ]:=dx;
     m_y[0 ]:=dy;

     force_redraw;

     exit;

    end;

   if m_idx >= 0 then
    begin
     m_x[m_idx ]:=x - m_dx;
     m_y[m_idx ]:=y - m_dy;

     force_redraw;

    end;

  end
 else
  on_mouse_button_up(x ,y ,flags );

end;

{ ON_MOUSE_BUTTON_DOWN }
procedure the_application.on_mouse_button_down;
var
 i : unsigned;

begin
 if flags and mouse_left <> 0 then
  begin
   i:=0;

   while i < 3 do
    begin
     if Sqrt((x - m_x[i ] ) * (x - m_x[i ] ) + (y - m_y[i ] ) * (y - m_y[i ] ) ) < 20.0 then
      begin
       m_dx :=x - m_x[i ];
       m_dy :=y - m_y[i ];
       m_idx:=i;

       break;

      end;

     inc(i );

    end;

   if i = 3 then
    if point_in_triangle(
        m_x[0 ] ,m_y[0 ] ,
        m_x[1 ] ,m_y[1 ] ,
        m_x[2 ] ,m_y[2 ] ,x ,y ) then
     begin
      m_dx :=x - m_x[0 ];
      m_dy :=y - m_y[0 ];
      m_idx:=3;

     end;

  end;

end;

{ ON_MOUSE_BUTTON_UP }
procedure the_application.on_mouse_button_up;
begin
 m_idx:=-1;

end;

{ ON_KEY }
procedure the_application.on_key;
var
 dx ,dy : double;

begin
 dx:=0;
 dy:=0;

 case key of
  key_left  : dx:=-0.1;
  key_right : dx:= 0.1;
  key_up    : dy:= 0.1;
  key_down  : dy:=-0.1;

 end;

 m_x[0 ]:=m_x[0 ] + dx;
 m_y[0 ]:=m_y[0 ] + dy;
 m_x[1 ]:=m_x[1 ] + dx;
 m_y[1 ]:=m_y[1 ] + dy;

 force_redraw;

 if key = key_f1 then
  message_(
   'Another example that demonstrates the power of the custom pipeline concept. '#13 +
   'First, we calculate a thick outline (stroke), then generate dashes, and then, '#13 +
   'calculate the outlines (strokes) of the dashes again.'#13#13 +
   'How to play with:'#13#13 +
   'Use the left mouse in the corners of the triangle to move the particular vertices.'#13 +
   'Drag and move the whole picture with mouse or arrow keys.' +
   #13#13'Note: F2 key saves current "screenshot" file in this demo''s directory.  ' );

end;

VAR
 app : the_application;

BEGIN
 app.Construct(pix_format_bgr24 ,flip_y );
 app.caption_ ('AGG Example. Line Join (F1-Help)' );

 if app.init(500 ,330 ,0 ) then
  app.run;

 app.Destruct;

END.