Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
lazarus-project / usr / share / lazarus / 2.0.10 / components / aggpas / src / agg_trans_affine.pas
Size: Mime:
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4 (Public License)
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3)
// Pascal Port By: Milan Marusinec alias Milano
//                 milan@marusinec.sk
//                 http://www.aggpas.org
// Copyright (c) 2005-2006
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//
//----------------------------------------------------------------------------
//
// Affine transformation classes.
//
// [Pascal Port History] -----------------------------------------------------
//
// 26.10.2007-Milano: Reflection Transformations
// 27.09.2005-Milano: Complete unit port
//
//----------------------------------------------------------------------------
//
// Affine transformation are linear transformations in Cartesian coordinates
// (strictly speaking not only in Cartesian, but for the beginning we will
// think so). They are rotation, scaling, translation and skewing.
// After any affine transformation a line segment remains a line segment
// and it will never become a curve.
//
// There will be no math about matrix calculations, since it has been
// described many times. Ask yourself a very simple question:
// "why do we need to understand and use some matrix stuff instead of just
// rotating, scaling and so on". The answers are:
//
// 1. Any combination of transformations can be done by only 4 multiplications
//    and 4 additions in floating point.
// 2. One matrix transformation is equivalent to the number of consecutive
//    discrete transformations, i.e. the matrix "accumulates" all transformations
//    in the order of their settings. Suppose we have 4 transformations:
//       * rotate by 30 degrees,
//       * scale X to 2.0,
//       * scale Y to 1.5,
//       * move to (100, 100).
//    The result will depend on the order of these transformations,
//    and the advantage of matrix is that the sequence of discret calls:
//    rotate(30), scaleX(2.0), scaleY(1.5), move(100,100)
//    will have exactly the same result as the following matrix transformations:
//
//    affine_matrix m;
//    m *= rotate_matrix(30);
//    m *= scaleX_matrix(2.0);
//    m *= scaleY_matrix(1.5);
//    m *= move_matrix(100,100);
//
//    m.transform_my_point_at_last(x, y);
//
// What is the good of it? In real life we will set-up the matrix only once
// and then transform many points, let alone the convenience to set any
// combination of transformations.
//
// So, how to use it? Very easy - literally as it's shown above. Not quite,
// let us write a correct example:
//
// agg::trans_affine m;
// m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0);
// m *= agg::trans_affine_scaling(2.0, 1.5);
// m *= agg::trans_affine_translation(100.0, 100.0);
// m.transform(&x, &y);
//
// The affine matrix is all you need to perform any linear transformation,
// but all transformations have origin point (0,0). It means that we need to
// use 2 translations if we want to rotate someting around (100,100):
//
// m *= agg::trans_affine_translation(-100.0, -100.0);         // move to (0,0)
// m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0);  // rotate
// m *= agg::trans_affine_translation(100.0, 100.0);           // move back to (100,100)
//
{ agg_trans_affine.pas }
unit
 agg_trans_affine ;

INTERFACE

{$I agg_mode.inc }

uses
 Math ,
 agg_basics ;

{ TYPES DEFINITION }
const
 affine_epsilon = 1e-14; // About of precision of doubles

type
 trans_affine_ptr = ^trans_affine;

 proc_transform = procedure(this : trans_affine_ptr; x ,y : double_ptr );

 parallelo_ptr = ^parallelogram;
 parallelogram = array[0..5 ] of double;

 trans_affine = object
 { sx ,shy ,shx ,sy ,tx ,ty }
   m0 ,m1  ,m2  ,m3 ,m4 ,m5 : double;

   transform         ,
   transform_2x2     ,
   inverse_transform : proc_transform;

  // Construct an identity matrix - it does not transform anything
   constructor Construct; overload;

  // Construct a custom matrix. Usually used in derived classes
   constructor Construct(v0 ,v1 ,v2 ,v3 ,v4 ,v5 : double ); overload;

  // Construct a matrix to transform a parallelogram to another one
   constructor Construct(rect, parl : parallelo_ptr ); overload;

  // Construct a matrix to transform a rectangle to a parallelogram
   constructor Construct(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr ); overload;

  // Construct a matrix to transform a parallelogram to a rectangle
   constructor Construct(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double ); overload;

  // Construct a matrix with different transform function
   constructor Construct(tr : proc_transform ); overload;

  //---------------------------------- Parallelogram transformations
  // Calculate a matrix to transform a parallelogram to another one.
  // src and dst are pointers to arrays of three points
  // (double[6], x,y,...) that identify three corners of the
  // parallelograms assuming implicit fourth points.
  // There are also transformations rectangtle to parallelogram and
  // parellelogram to rectangle
   procedure parl_to_parl(src ,dst : parallelo_ptr );
   procedure rect_to_parl(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr );
   procedure parl_to_rect(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double );

  //------------------------------------------ Operations
  // Reset - actually load an identity matrix
   procedure reset; virtual;

  // Multiply matrix to another one
   procedure multiply(m : trans_affine_ptr );

  // Multiply "m" to "this" and assign the result to "this"
   procedure premultiply(m : trans_affine_ptr );

  // Multiply matrix to inverse of another one
   procedure multiply_inv(m : trans_affine_ptr );

  // Multiply inverse of "m" to "this" and assign the result to "this"
   procedure premultiply_inv(m : trans_affine_ptr );

  // Invert matrix. Do not try to invert degenerate matrices,
  // there's no check for validity. If you set scale to 0 and
  // then try to invert matrix, expect unpredictable result.
   procedure invert;

  // Mirroring around X
   procedure flip_x;

  // Mirroring around Y
   procedure flip_y;

  //------------------------------------------- Load/Store
  // Store matrix to an array [6] of double
   procedure store_to(m : parallelo_ptr );

  // Load matrix from an array [6] of double
   procedure load_from(m : parallelo_ptr );

  //-------------------------------------------- Transformations
  // Direct transformation x and y
  // see: transform : proc_transform; above

  // Direct transformation x and y, 2x2 matrix only, no translation
  // procedure transform_2x2(x ,y : double_ptr );

  // Inverse transformation x and y. It works slower than the
  // direct transformation, so if the performance is critical
  // it's better to invert() the matrix and then use transform()
  // procedure inverse_transform(x ,y : double_ptr );

  //-------------------------------------------- Auxiliary
  // Calculate the determinant of matrix
   function  determinant : double;

  // Get the average scale (by X and Y).
  // Basically used to calculate the approximation_scale when
  // decomposinting curves into line segments.
   function  scale : double; overload;

  // Check to see if it's an identity matrix
   function  is_identity(epsilon : double = affine_epsilon ) : boolean;

  // Check to see if two matrices are equal
   function  is_equal(m : trans_affine; epsilon : double = affine_epsilon ) : boolean;

  // Determine the major parameters. Use carefully considering degenerate matrices
   function  rotation : double;
   procedure translation(dx ,dy : double_ptr );
   procedure scaling    (sx ,sy : double_ptr );
   procedure scaling_abs(sx ,sy : double_ptr );

  // Trans Affine Assignations
   procedure assign    (from : trans_affine_ptr );
   procedure assign_all(from : trans_affine_ptr );

  // Direct transformations operations
   function  translate(x ,y : double ) : trans_affine_ptr;
   function  rotate   (a : double ) : trans_affine_ptr;
   function  scale    (s : double ) : trans_affine_ptr; overload;
   function  scale    (x ,y : double ) : trans_affine_ptr; overload;

  end;

//====================================================trans_affine_rotation
// Rotation matrix. sin() and cos() are calculated twice for the same angle.
// There's no harm because the performance of sin()/cos() is very good on all
// modern processors. Besides, this operation is not going to be invoked too
// often.
 trans_affine_rotation = object(trans_affine )
   constructor Construct(a : double );

  end;

//====================================================trans_affine_scaling
// Scaling matrix. sx, sy - scale coefficients by X and Y respectively
 trans_affine_scaling = object(trans_affine )
   constructor Construct(sx ,sy : double ); overload;
   constructor Construct(s : double ); overload;

  end;

//================================================trans_affine_translation
// Translation matrix
 trans_affine_translation = object(trans_affine )
   constructor Construct(tx ,ty : double );

  end;

//====================================================trans_affine_skewing
// Sckewing (shear) matrix
 trans_affine_skewing = object(trans_affine )
   constructor Construct(sx ,sy : double );

  end;

//===============================================trans_affine_line_segment
// Rotate, Scale and Translate, associating 0...dist with line segment
// x1,y1,x2,y2
 trans_affine_line_segment = object(trans_affine )
   constructor Construct(x1 ,y1 ,x2 ,y2 ,dist : double );

  end;

//============================================trans_affine_reflection_unit
// Reflection matrix. Reflect coordinates across the line through
// the origin containing the unit vector (ux, uy).
// Contributed by John Horigan
 trans_affine_reflection_unit = object(trans_affine )
   constructor Construct(ux ,uy : double );

  end;

//=================================================trans_affine_reflection
// Reflection matrix. Reflect coordinates across the line through
// the origin at the angle a or containing the non-unit vector (x, y).
// Contributed by John Horigan
 trans_affine_reflection = object(trans_affine_reflection_unit )
   constructor Construct(a : double ); overload;
   constructor Construct(x ,y : double ); overload;

  end;

{ GLOBAL PROCEDURES }
 function  is_equal_eps(v1 ,v2 ,epsilon : double ) : boolean;


IMPLEMENTATION
{ UNIT IMPLEMENTATION }
{ is_equal_eps }
function is_equal_eps;
begin
 result:=Abs(v1 - v2 ) < epsilon;

end;

{ trans_affine_transform }
procedure trans_affine_transform(this : trans_affine_ptr; x ,y : double_ptr );
var
 tx : double;

begin
 tx:=x^;
 x^:=tx * this.m0 + y^ * this.m2 + this.m4;
 y^:=tx * this.m1 + y^ * this.m3 + this.m5;

end;

{ trans_affine_transform_2x2 }
procedure trans_affine_transform_2x2(this : trans_affine_ptr; x ,y : double_ptr );
var
 tx : double;

begin
 tx:=x^;
 x^:=tx * this.m0 + y^ * this.m2;
 y^:=tx * this.m1 + y^ * this.m3;

end;

{ trans_affine_inverse_transform }
procedure trans_affine_inverse_transform(this : trans_affine_ptr; x ,y : double_ptr );
var
 d ,a ,b : double;

begin
 d:=this.determinant;
 a:=(x^ - this.m4 ) * d;
 b:=(y^ - this.m5 ) * d;

 x^:=a * this.m3 - b * this.m2;
 y^:=b * this.m0 - a * this.m1;

end;

{ CONSTRUCT }
constructor trans_affine.Construct;
begin
 m0:=1;
 m1:=0;
 m2:=0;
 m3:=1;
 m4:=0;
 m5:=0;

 transform        :=@trans_affine_transform;
 transform_2x2    :=@trans_affine_transform_2x2;
 inverse_transform:=@trans_affine_inverse_transform;

end;

{ CONSTRUCT }
constructor trans_affine.Construct(v0 ,v1 ,v2 ,v3 ,v4 ,v5 : double );
begin
 m0:=v0;
 m1:=v1;
 m2:=v2;
 m3:=v3;
 m4:=v4;
 m5:=v5;

 transform        :=@trans_affine_transform;
 transform_2x2    :=@trans_affine_transform_2x2;
 inverse_transform:=@trans_affine_inverse_transform;

end;

{ CONSTRUCT }
constructor trans_affine.Construct(rect, parl : parallelo_ptr );
begin
 parl_to_parl(rect ,parl );

 transform        :=@trans_affine_transform;
 transform_2x2    :=@trans_affine_transform_2x2;
 inverse_transform:=@trans_affine_inverse_transform;

end;

{ CONSTRUCT }
constructor trans_affine.Construct(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr );
begin
 rect_to_parl(x1 ,y1 ,x2 ,y2 ,parl );

 transform        :=@trans_affine_transform;
 transform_2x2    :=@trans_affine_transform_2x2;
 inverse_transform:=@trans_affine_inverse_transform;

end;

{ CONSTRUCT }
constructor trans_affine.Construct(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double );
begin
 parl_to_rect(parl ,x1 ,y1 ,x2 ,y2 );

 transform        :=@trans_affine_transform;
 transform_2x2    :=@trans_affine_transform_2x2;
 inverse_transform:=@trans_affine_inverse_transform;

end;

{ CONSTRUCT }
constructor trans_affine.Construct(tr : proc_transform );
begin
 m0:=1;
 m1:=0;
 m2:=0;
 m3:=1;
 m4:=0;
 m5:=0;

 transform        :=tr;
 transform_2x2    :=@trans_affine_transform_2x2;
 inverse_transform:=@trans_affine_inverse_transform;

end;

{ parl_to_parl }
procedure trans_affine.parl_to_parl;
var
 m : trans_affine;

begin
 m0:=src[2 ] - src[0 ];
 m1:=src[3 ] - src[1 ];
 m2:=src[4 ] - src[0 ];
 m3:=src[5 ] - src[1 ];
 m4:=src[0 ];
 m5:=src[1 ];

 invert;

 m.Construct(
  dst[2 ] - dst[0 ] ,
  dst[3 ] - dst[1 ] ,
  dst[4 ] - dst[0 ] ,
  dst[5 ] - dst[1 ] ,
  dst[0 ] ,
  dst[1 ] );

 multiply(@m );

end;

{ rect_to_parl }
procedure trans_affine.rect_to_parl;
var
 src : parallelogram;

begin
 src[0 ]:=x1;
 src[1 ]:=y1;
 src[2 ]:=x2;
 src[3 ]:=y1;
 src[4 ]:=x2;
 src[5 ]:=y2;

 parl_to_parl(@src ,parl );

end;

{ parl_to_rect }
procedure trans_affine.parl_to_rect;
var
 dst : parallelogram;

begin
 dst[0 ]:=x1;
 dst[1 ]:=y1;
 dst[2 ]:=x2;
 dst[3 ]:=y1;
 dst[4 ]:=x2;
 dst[5 ]:=y2;

 parl_to_parl(parl ,@dst );

end;

{ reset }
procedure trans_affine.reset;
begin
 m0:=1;
 m1:=0;
 m2:=0;
 m3:=1;
 m4:=0;
 m5:=0;

end;

{ multiply }
procedure trans_affine.multiply;
var
 t0 ,t2 ,t4 : double;

begin
 t0:=m0 * m.m0 + m1 * m.m2;
 t2:=m2 * m.m0 + m3 * m.m2;
 t4:=m4 * m.m0 + m5 * m.m2 + m.m4;
 m1:=m0 * m.m1 + m1 * m.m3;
 m3:=m2 * m.m1 + m3 * m.m3;
 m5:=m4 * m.m1 + m5 * m.m3 + m.m5;
 m0:=t0;
 m2:=t2;
 m4:=t4;

end;

{ premultiply }
procedure trans_affine.premultiply;
var
 t : trans_affine;

begin
 t.assign_all(m );

 t.multiply(@self );

 assign(@t );

end;

{ multiply_inv }
procedure trans_affine.multiply_inv;
var
 t : trans_affine;

begin
 t.assign_all(m );
 t.invert;

 multiply(@t );

end;

{ premultiply_inv }
procedure trans_affine.premultiply_inv;
var
 t : trans_affine;

begin
 t.assign_all(m );

 t.invert;
 t.multiply(@self );

 assign(@t );

end;

{ invert }
procedure trans_affine.invert;
var
 d ,t0 ,t4 : double;

begin
 d:=determinant;

 t0:= m3 * d;
 m3:= m0 * d;
 m1:=-m1 * d;
 m2:=-m2 * d;

 t4:=-m4 * t0 - m5 * m2;
 m5:=-m4 * m1 - m5 * m3;

 m0:=t0;
 m4:=t4;

end;

{ flip_x }
procedure trans_affine.flip_x;
begin
 m0:=-m0;
 m1:=-m1;
 m4:=-m4;

end;

{ flip_y }
procedure trans_affine.flip_y;
begin
 m2:=-m2;
 m3:=-m3;
 m5:=-m5;

end;

{ store_to }
procedure trans_affine.store_to;
begin
 m[0 ]:=m0;
 m[1 ]:=m1;
 m[2 ]:=m2;
 m[3 ]:=m3;
 m[4 ]:=m4;
 m[5 ]:=m5;

end;

{ load_from }
procedure trans_affine.load_from;
begin
 m0:=m[0 ];
 m1:=m[1 ];
 m2:=m[2 ];
 m3:=m[3 ];
 m4:=m[4 ];
 m5:=m[5 ];

end;

{ determinant }
function trans_affine.determinant;
begin
 try
  result:=1 / (m0 * m3 - m1 * m2 );

 except
  result:=0;

 end;

end;

{ scale }
function trans_affine.scale : double;
var
 x ,y : double;

begin
 x:=0.707106781 * m0 + 0.707106781 * m2;
 y:=0.707106781 * m1 + 0.707106781 * m3;

 result:=Sqrt(x * x + y * y );

end;

{ is_identity }
function trans_affine.is_identity;
begin
 result:=
  is_equal_eps(m0 ,1 ,epsilon ) and
  is_equal_eps(m1 ,0 ,epsilon ) and
  is_equal_eps(m2 ,0 ,epsilon ) and
  is_equal_eps(m3 ,1 ,epsilon ) and
  is_equal_eps(m4 ,0 ,epsilon ) and
  is_equal_eps(m5 ,0 ,epsilon );

end;

{ is_equal }
function trans_affine.is_equal;
begin
 result:=
  is_equal_eps(m0 ,m.m0 ,epsilon ) and
  is_equal_eps(m1 ,m.m1 ,epsilon ) and
  is_equal_eps(m2 ,m.m2 ,epsilon ) and
  is_equal_eps(m3 ,m.m3 ,epsilon ) and
  is_equal_eps(m4 ,m.m4 ,epsilon ) and
  is_equal_eps(m5 ,m.m5 ,epsilon );
  
end;

{ rotation }
function trans_affine.rotation;
var
 x1 ,y1 ,x2 ,y2 : double;

begin
 x1:=0;
 y1:=0;
 x2:=1;
 y2:=0;

 transform(@self ,@x1 ,@y1 );
 transform(@self ,@x2 ,@y2 );

 result:=ArcTan2(y2 - y1 ,x2 - x1 );

end;

{ translation }
procedure trans_affine.translation;
begin
 dx:=0;
 dy:=0;

 transform(@self ,@dx ,@dy );

end;

{ scaling }
procedure trans_affine.scaling;
var
 t : trans_affine_rotation;

 x1 ,y1 ,x2 ,y2 : double;

begin
 x1:=0;
 y1:=0;
 x2:=1;
 y2:=1;

 trans_affine(t ):=self;

 t.Construct(-rotation );

 t.transform(@self ,@x1 ,@y1 );
 t.transform(@self ,@x2 ,@y2 );

 sx^:=x2 - x1;
 sy^:=y2 - y1;

end;

{ scaling_abs }
procedure trans_affine.scaling_abs;
begin
 sx^:=Sqrt(m0 * m0 + m2 * m2 );
 sy^:=Sqrt(m1 * m1 + m3 * m3 );

end;

{ ASSIGN }
procedure trans_affine.assign;
begin
 m0:=from.m0;
 m1:=from.m1;
 m2:=from.m2;
 m3:=from.m3;
 m4:=from.m4;
 m5:=from.m5;

end;

{ ASSIGN_ALL }
procedure trans_affine.assign_all;
begin
 m0:=from.m0;
 m1:=from.m1;
 m2:=from.m2;
 m3:=from.m3;
 m4:=from.m4;
 m5:=from.m5;

 transform        :=@from.transform;
 transform_2x2    :=@from.transform_2x2;
 inverse_transform:=@from.inverse_transform;

end;

{ TRANSLATE }
function trans_affine.translate(x ,y : double ) : trans_affine_ptr;
begin
 m4:=m4 + x;
 m5:=m5 + y;

 result:=@self;

end;

{ ROTATE }
function trans_affine.rotate(a : double ) : trans_affine_ptr;
var
 ca ,sa ,t0 ,t2 ,t4 : double;

begin
 ca:=Cos(a);
 sa:=Sin(a);
 t0:=m0 * ca - m1 * sa;
 t2:=m2 * ca - m3 * sa;
 t4:=m4 * ca - m5 * sa;

 m1:=m0 * sa + m1 * ca;
 m3:=m2 * sa + m3 * ca;
 m5:=m4 * sa + m5 * ca;
 m0:=t0;
 m2:=t2;
 m4:=t4;

 result:=@self;

end;

{ SCALE }
function trans_affine.scale(s : double ) : trans_affine_ptr;
begin
 m0:=m0 * s;
 m1:=m1 * s;
 m2:=m2 * s;
 m3:=m3 * s;
 m4:=m4 * s;
 m5:=m5 * s;

 result:=@self;

end;

{ SCALE }
function trans_affine.scale(x ,y : double ) : trans_affine_ptr;
begin
 m0:=m0 * x;
 m2:=m2 * x;
 m4:=m4 * x;
 m1:=m1 * y;
 m3:=m3 * y;
 m5:=m5 * y;

 result:=@self;

end;

{ CONSTRUCT }
constructor trans_affine_rotation.Construct;
begin
 inherited Construct(Cos(a ) ,Sin(a ) ,-Sin(a ) ,Cos(a ) ,0 ,0 );

end;

{ CONSTRUCT }
constructor trans_affine_scaling.Construct(sx ,sy : double );
begin
 inherited Construct(sx ,0 ,0 ,sy ,0 ,0 );

end;

{ CONSTRUCT }
constructor trans_affine_scaling.Construct(s : double );
begin
 inherited Construct(s ,0 ,0 ,s ,0 ,0 );

end;

{ CONSTRUCT }
constructor trans_affine_translation.Construct;
begin
 inherited Construct(1 ,0 ,0 ,1 ,tx ,ty );

end;

{ CONSTRUCT }
constructor trans_affine_skewing.Construct;
begin
 inherited Construct(1 ,Tan(sy ) ,Tan(sx ) ,1 ,0 ,0 );

end;

{ CONSTRUCT }
constructor trans_affine_line_segment.Construct;
var
 dx ,dy : double;

 s : trans_affine_scaling;
 r : trans_affine_rotation;
 t : trans_affine_translation;

begin
 dx:=x2 - x1;
 dy:=y2 - y1;

 if dist > 0 then
  begin
   s.Construct(Sqrt(dx * dx + dy * dy ) / dist );

   multiply(@s );

  end;

 r.Construct(ArcTan2(dy ,dx ) );

 multiply(@r );

 t.Construct(x1 ,y1 );

 multiply(@t );

end;

{ CONSTRUCT }
constructor trans_affine_reflection_unit.Construct(ux ,uy : double );
begin
 inherited Construct(
  2.0 * ux * ux - 1.0 ,
  2.0 * ux * uy ,
  2.0 * ux * uy ,
  2.0 * uy * uy - 1.0 ,
  0.0 ,0.0 );

end;

{ CONSTRUCT }
constructor trans_affine_reflection.Construct(a : double );
begin
 inherited Construct(Cos(a ) ,Sin(a ) );

end;

{ CONSTRUCT }
constructor trans_affine_reflection.Construct(x ,y : double );
var
 nx ,ny : double;

begin
 if (x = 0 ) and
    (y = 0 ) then
  begin
   x:=0;
   y:=0;

  end
 else
  begin
   nx:=x / Sqrt(x * x + y * y );
   ny:=y / Sqrt(x * x + y * y );

  end;

 inherited Construct(nx ,ny );

end;

END.