//
// 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_contour_ ;

uses
 agg_basics ,
 agg_platform_support ,

 agg_color ,
 agg_pixfmt ,
 agg_pixfmt_rgb ,

 agg_ctrl ,
 agg_rbox_ctrl ,
 agg_cbox_ctrl ,
 agg_slider_ctrl ,

 agg_rendering_buffer ,
 agg_renderer_base ,
 agg_renderer_scanline ,
 agg_rasterizer_scanline_aa ,
 agg_scanline_p ,
 agg_render_scanlines ,

 agg_conv_stroke ,
 agg_conv_transform ,
 agg_conv_curve ,
 agg_conv_contour ,
 agg_path_storage ,
 agg_trans_affine ,
 agg_math_stroke ;

{$I agg_mode.inc }

const
 flip_y = true;

type
 the_application = object(platform_support )
   m_close       : rbox_ctrl;
   m_width       : slider_ctrl;
   m_auto_detect : cbox_ctrl;
   m_path        : path_storage;

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

   procedure compose_path;
   procedure on_draw; virtual;

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

  end;

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

 m_close.Construct      (10.0 ,10.0 ,130.0 ,80.0 ,not flip_y_ );
 m_width.Construct      (130 + 10.0 ,10.0 + 4.0 ,130 + 300.0 ,10.0 + 8.0 + 4.0 ,not flip_y_ );
 m_auto_detect.Construct(130 + 10.0 ,10.0 + 4.0 + 16.0 ,'Autodetect orientation if not defined' ,not flip_y_ );
 m_path.Construct;

 add_ctrl(@m_close );

 m_close.add_item ('Close' );
 m_close.add_item ('Close CW' );
 m_close.add_item ('Close CCW' );
 m_close.cur_item_(0 );

 add_ctrl(@m_width );

 m_width.range_(-100.0 ,100.0 );
 m_width.value_(0.0 );
 m_width.label_('Width=%1.2f' );

 add_ctrl(@m_auto_detect );

end;

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

 m_close.Destruct;
 m_width.Destruct;
 m_auto_detect.Destruct;
 m_path.Destruct;

end;

{ COMPOSE_PATH }
procedure the_application.compose_path;
var
 flag : unsigned;

begin
 flag:=0;

 if m_close._cur_item = 1 then
  flag:=path_flags_cw;

 if m_close._cur_item = 2 then
  flag:=path_flags_ccw;

 m_path.remove_all;

 m_path.move_to(28.47 ,6.45 );
 m_path.curve3 (21.58 ,1.12  ,19.82 ,0.29 );
 m_path.curve3 (17.19 ,-0.93 ,14.21 ,-0.93 );
 m_path.curve3 (9.57  ,-0.93 ,6.57  ,2.25 );
 m_path.curve3 (3.56  ,5.42  ,3.56  ,10.60 );
 m_path.curve3 (3.56  ,13.87 ,5.03  ,16.26 );
 m_path.curve3 (7.03  ,19.58 ,11.99 ,22.51 );
 m_path.curve3 (16.94 ,25.44 ,28.47 ,29.64 );
 m_path.line_to(28.47 ,31.40 );
 m_path.curve3 (28.47 ,38.09 ,26.34 ,40.58 );
 m_path.curve3 (24.22 ,43.07 ,20.17 ,43.07 );
 m_path.curve3 (17.09 ,43.07 ,15.28 ,41.41 );
 m_path.curve3 (13.43 ,39.75 ,13.43 ,37.60 );
 m_path.line_to(13.53 ,34.77 );
 m_path.curve3 (13.53 ,32.52 ,12.38 ,31.30 );
 m_path.curve3 (11.23 ,30.08 ,9.38  ,30.08 );
 m_path.curve3 (7.57  ,30.08 ,6.42  ,31.35 );
 m_path.curve3 (5.27  ,32.62 ,5.27  ,34.81 );
 m_path.curve3 (5.27  ,39.01 ,9.57  ,42.53 );
 m_path.curve3 (13.87 ,46.04 ,21.63 ,46.04 );
 m_path.curve3 (27.59 ,46.04 ,31.40 ,44.04 );
 m_path.curve3 (34.28 ,42.53 ,35.64 ,39.31 );
 m_path.curve3 (36.52 ,37.21 ,36.52 ,30.71 );
 m_path.line_to(36.52 ,15.53 );
 m_path.curve3 (36.52 ,9.13  ,36.77 ,7.69 );
 m_path.curve3 (37.01 ,6.25  ,37.57 ,5.76 );
 m_path.curve3 (38.13 ,5.27  ,38.87 ,5.27 );
 m_path.curve3 (39.65 ,5.27  ,40.23 ,5.62 );
 m_path.curve3 (41.26 ,6.25  ,44.19 ,9.18 );
 m_path.line_to(44.19 ,6.45  );
 m_path.curve3 (38.72 ,-0.88 ,33.74 ,-0.88 );
 m_path.curve3 (31.35 ,-0.88 ,29.93 ,0.78 );
 m_path.curve3 (28.52 ,2.44  ,28.47 ,6.45 );

 m_path.close_polygon(flag );

 m_path.move_to(28.47 ,9.62 );
 m_path.line_to(28.47 ,26.66 );
 m_path.curve3 (21.09 ,23.73 ,18.95 ,22.51 );
 m_path.curve3 (15.09 ,20.36 ,13.43 ,18.02 );
 m_path.curve3 (11.77 ,15.67 ,11.77 ,12.89 );
 m_path.curve3 (11.77 ,9.38  ,13.87 ,7.06 );
 m_path.curve3 (15.97 ,4.74  ,18.70 ,4.74 );
 m_path.curve3 (22.41 ,4.74  ,28.47 ,9.62 );

 m_path.close_polygon(flag );

end;

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

 ras : rasterizer_scanline_aa;
 sl  : scanline_p8;

 mtx : trans_affine;
 tas : trans_affine_scaling;
 tat : trans_affine_translation;

 trans : conv_transform;
 curve : conv_curve;

 contour : conv_contour;

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
 mtx.Construct;

 tas.Construct(4.0 );
 mtx.multiply (@tas );

 tat.Construct(150 ,100 );
 mtx.multiply (@tat );

 trans.Construct(@m_path ,@mtx );
 curve.Construct(@trans );

 contour.Construct(@curve );

 contour.width_                  (m_width._value );
 //contour.line_join_              (miter_join );
 //contour.inner_join_             (miter_join );
 //contour.inner_miter_limit_      (4.0 );
 contour.auto_detect_orientation_(m_auto_detect._status );

 compose_path;
 ras.add_path(@contour );

 rgba.ConstrDbl(0 ,0 ,0 );
 ren.color_    (@rgba );

 render_scanlines(@ras ,@sl ,@ren );

// Render the controls
 render_ctrl(@ras ,@sl ,@ren ,@m_close );
 render_ctrl(@ras ,@sl ,@ren ,@m_width );
 render_ctrl(@ras ,@sl ,@ren ,@m_auto_detect );

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

 curve.Destruct;
 contour.Destruct;

end;

{ ON_KEY }
procedure the_application.on_key;
begin
 if key = key_f1 then
  message_(
   'One of the converters in AGG is conv_contour. It allows you to extend or shrink '#13 +
   'polygons. Initially, it was implemented to eliminate the "problem of adjacent edges"'#13 +
   'in the SVG Viewer, but it can be very useful in many other applications, for example,'#13 +
   'to change the font weight on the fly. The trick here is that the sign (dilation or '#13 +
   'shrinking) depends on the vertex order - clockwise or counterclockwise. '#13 +
   'In the conv_contour you can control the behavior. Sometimes you need to preserve '#13 +
   'the dilation regardless of the initial orientation, sometimes it should depend '#13 +
   'on the orientation. The glyph ‘a’ has a "hole" whose orientation differs from '#13 +
   'the main contour. To change the "weight" correctly, you need to keep the orientation '#13 +
   'as it is originally defined. If you turn "Autodetect orientation…" on, the glyph will '#13 +
   'be extended or shrinked incorrectly.'#13#13 +
   'How to play with:'#13#13 +
   'The radio buttons control the orientation flad assigned to all polygons.'#13 +
   '"Close" doesn''t add the flag.'#13 +
   '"Close CW" and "Close CCW" add "clockwise" or "counterclockwise" flag respectively.' +
   #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. Contour Tool & Polygon Orientation (F1-Help)' );

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

 app.Destruct;

END.