1
//----------------------------------------------------------------------------
2
// Anti-Grain Geometry - Version 2.4 (Public License)
3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
5
// Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3)
6
// Pascal Port By: Milan Marusinec alias Milano
8
// http://www.aggpas.org
9
// Copyright (c) 2005-2006
11
// Permission to copy, use, modify, sell and distribute this software
12
// is granted provided this copyright notice appears in all copies.
13
// This software is provided "as is" without express or implied
14
// warranty, and with no claim as to its suitability for any purpose.
16
//----------------------------------------------------------------------------
17
// Contact: mcseem@antigrain.com
18
// mcseemagg@yahoo.com
19
// http://www.antigrain.com
21
//----------------------------------------------------------------------------
23
// Affine transformation classes.
25
// [Pascal Port History] -----------------------------------------------------
27
// 26.10.2007-Milano: Reflection Transformations
28
// 27.09.2005-Milano: Complete unit port
30
//----------------------------------------------------------------------------
32
// Affine transformation are linear transformations in Cartesian coordinates
33
// (strictly speaking not only in Cartesian, but for the beginning we will
34
// think so). They are rotation, scaling, translation and skewing.
35
// After any affine transformation a line segment remains a line segment
36
// and it will never become a curve.
38
// There will be no math about matrix calculations, since it has been
39
// described many times. Ask yourself a very simple question:
40
// "why do we need to understand and use some matrix stuff instead of just
41
// rotating, scaling and so on". The answers are:
43
// 1. Any combination of transformations can be done by only 4 multiplications
44
// and 4 additions in floating point.
45
// 2. One matrix transformation is equivalent to the number of consecutive
46
// discrete transformations, i.e. the matrix "accumulates" all transformations
47
// in the order of their settings. Suppose we have 4 transformations:
48
// * rotate by 30 degrees,
51
// * move to (100, 100).
52
// The result will depend on the order of these transformations,
53
// and the advantage of matrix is that the sequence of discret calls:
54
// rotate(30), scaleX(2.0), scaleY(1.5), move(100,100)
55
// will have exactly the same result as the following matrix transformations:
58
// m *= rotate_matrix(30);
59
// m *= scaleX_matrix(2.0);
60
// m *= scaleY_matrix(1.5);
61
// m *= move_matrix(100,100);
63
// m.transform_my_point_at_last(x, y);
65
// What is the good of it? In real life we will set-up the matrix only once
66
// and then transform many points, let alone the convenience to set any
67
// combination of transformations.
69
// So, how to use it? Very easy - literally as it's shown above. Not quite,
70
// let us write a correct example:
72
// agg::trans_affine m;
73
// m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0);
74
// m *= agg::trans_affine_scaling(2.0, 1.5);
75
// m *= agg::trans_affine_translation(100.0, 100.0);
76
// m.transform(&x, &y);
78
// The affine matrix is all you need to perform any linear transformation,
79
// but all transformations have origin point (0,0). It means that we need to
80
// use 2 translations if we want to rotate someting around (100,100):
82
// m *= agg::trans_affine_translation(-100.0, -100.0); // move to (0,0)
83
// m *= agg::trans_affine_rotation(30.0 * 3.1415926 / 180.0); // rotate
84
// m *= agg::trans_affine_translation(100.0, 100.0); // move back to (100,100)
86
{ agg_trans_affine.pas }
100
affine_epsilon = 1e-14; // About of precision of doubles
103
trans_affine_ptr = ^trans_affine;
105
proc_transform = procedure(this : trans_affine_ptr; x ,y : double_ptr );
107
parallelo_ptr = ^parallelogram;
108
parallelogram = array[0..5 ] of double;
110
trans_affine = object
111
{ sx ,shy ,shx ,sy ,tx ,ty }
112
m0 ,m1 ,m2 ,m3 ,m4 ,m5 : double;
116
inverse_transform : proc_transform;
118
// Construct an identity matrix - it does not transform anything
119
constructor Construct; overload;
121
// Construct a custom matrix. Usually used in derived classes
122
constructor Construct(v0 ,v1 ,v2 ,v3 ,v4 ,v5 : double ); overload;
124
// Construct a matrix to transform a parallelogram to another one
125
constructor Construct(rect, parl : parallelo_ptr ); overload;
127
// Construct a matrix to transform a rectangle to a parallelogram
128
constructor Construct(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr ); overload;
130
// Construct a matrix to transform a parallelogram to a rectangle
131
constructor Construct(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double ); overload;
133
// Construct a matrix with different transform function
134
constructor Construct(tr : proc_transform ); overload;
136
//---------------------------------- Parallelogram transformations
137
// Calculate a matrix to transform a parallelogram to another one.
138
// src and dst are pointers to arrays of three points
139
// (double[6], x,y,...) that identify three corners of the
140
// parallelograms assuming implicit fourth points.
141
// There are also transformations rectangtle to parallelogram and
142
// parellelogram to rectangle
143
procedure parl_to_parl(src ,dst : parallelo_ptr );
144
procedure rect_to_parl(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr );
145
procedure parl_to_rect(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double );
147
//------------------------------------------ Operations
148
// Reset - actually load an identity matrix
149
procedure reset; virtual;
151
// Multiply matrix to another one
152
procedure multiply(m : trans_affine_ptr );
154
// Multiply "m" to "this" and assign the result to "this"
155
procedure premultiply(m : trans_affine_ptr );
157
// Multiply matrix to inverse of another one
158
procedure multiply_inv(m : trans_affine_ptr );
160
// Multiply inverse of "m" to "this" and assign the result to "this"
161
procedure premultiply_inv(m : trans_affine_ptr );
163
// Invert matrix. Do not try to invert degenerate matrices,
164
// there's no check for validity. If you set scale to 0 and
165
// then try to invert matrix, expect unpredictable result.
168
// Mirroring around X
171
// Mirroring around Y
174
//------------------------------------------- Load/Store
175
// Store matrix to an array [6] of double
176
procedure store_to(m : parallelo_ptr );
178
// Load matrix from an array [6] of double
179
procedure load_from(m : parallelo_ptr );
181
//-------------------------------------------- Transformations
182
// Direct transformation x and y
183
// see: transform : proc_transform; above
185
// Direct transformation x and y, 2x2 matrix only, no translation
186
// procedure transform_2x2(x ,y : double_ptr );
188
// Inverse transformation x and y. It works slower than the
189
// direct transformation, so if the performance is critical
190
// it's better to invert() the matrix and then use transform()
191
// procedure inverse_transform(x ,y : double_ptr );
193
//-------------------------------------------- Auxiliary
194
// Calculate the determinant of matrix
195
function determinant : double;
197
// Get the average scale (by X and Y).
198
// Basically used to calculate the approximation_scale when
199
// decomposinting curves into line segments.
200
function scale : double; overload;
202
// Check to see if it's an identity matrix
203
function is_identity(epsilon : double = affine_epsilon ) : boolean;
205
// Check to see if two matrices are equal
206
function is_equal(m : trans_affine; epsilon : double = affine_epsilon ) : boolean;
208
// Determine the major parameters. Use carefully considering degenerate matrices
209
function rotation : double;
210
procedure translation(dx ,dy : double_ptr );
211
procedure scaling (sx ,sy : double_ptr );
212
procedure scaling_abs(sx ,sy : double_ptr );
214
// Trans Affine Assignations
215
procedure assign (from : trans_affine_ptr );
216
procedure assign_all(from : trans_affine_ptr );
218
// Direct transformations operations
219
function translate(x ,y : double ) : trans_affine_ptr;
220
function rotate (a : double ) : trans_affine_ptr;
221
function scale (s : double ) : trans_affine_ptr; overload;
222
function scale (x ,y : double ) : trans_affine_ptr; overload;
226
//====================================================trans_affine_rotation
227
// Rotation matrix. sin() and cos() are calculated twice for the same angle.
228
// There's no harm because the performance of sin()/cos() is very good on all
229
// modern processors. Besides, this operation is not going to be invoked too
231
trans_affine_rotation = object(trans_affine )
232
constructor Construct(a : double );
236
//====================================================trans_affine_scaling
237
// Scaling matrix. sx, sy - scale coefficients by X and Y respectively
238
trans_affine_scaling = object(trans_affine )
239
constructor Construct(sx ,sy : double ); overload;
240
constructor Construct(s : double ); overload;
244
//================================================trans_affine_translation
245
// Translation matrix
246
trans_affine_translation = object(trans_affine )
247
constructor Construct(tx ,ty : double );
251
//====================================================trans_affine_skewing
252
// Sckewing (shear) matrix
253
trans_affine_skewing = object(trans_affine )
254
constructor Construct(sx ,sy : double );
258
//===============================================trans_affine_line_segment
259
// Rotate, Scale and Translate, associating 0...dist with line segment
261
trans_affine_line_segment = object(trans_affine )
262
constructor Construct(x1 ,y1 ,x2 ,y2 ,dist : double );
266
//============================================trans_affine_reflection_unit
267
// Reflection matrix. Reflect coordinates across the line through
268
// the origin containing the unit vector (ux, uy).
269
// Contributed by John Horigan
270
trans_affine_reflection_unit = object(trans_affine )
271
constructor Construct(ux ,uy : double );
275
//=================================================trans_affine_reflection
276
// Reflection matrix. Reflect coordinates across the line through
277
// the origin at the angle a or containing the non-unit vector (x, y).
278
// Contributed by John Horigan
279
trans_affine_reflection = object(trans_affine_reflection_unit )
280
constructor Construct(a : double ); overload;
281
constructor Construct(x ,y : double ); overload;
285
{ GLOBAL PROCEDURES }
286
function is_equal_eps(v1 ,v2 ,epsilon : double ) : boolean;
290
{ UNIT IMPLEMENTATION }
292
function is_equal_eps;
294
result:=Abs(v1 - v2 ) < epsilon;
298
{ trans_affine_transform }
299
procedure trans_affine_transform(this : trans_affine_ptr; x ,y : double_ptr );
305
x^:=tx * this.m0 + y^ * this.m2 + this.m4;
306
y^:=tx * this.m1 + y^ * this.m3 + this.m5;
310
{ trans_affine_transform_2x2 }
311
procedure trans_affine_transform_2x2(this : trans_affine_ptr; x ,y : double_ptr );
317
x^:=tx * this.m0 + y^ * this.m2;
318
y^:=tx * this.m1 + y^ * this.m3;
322
{ trans_affine_inverse_transform }
323
procedure trans_affine_inverse_transform(this : trans_affine_ptr; x ,y : double_ptr );
329
a:=(x^ - this.m4 ) * d;
330
b:=(y^ - this.m5 ) * d;
332
x^:=a * this.m3 - b * this.m2;
333
y^:=b * this.m0 - a * this.m1;
338
constructor trans_affine.Construct;
347
transform :=@trans_affine_transform;
348
transform_2x2 :=@trans_affine_transform_2x2;
349
inverse_transform:=@trans_affine_inverse_transform;
354
constructor trans_affine.Construct(v0 ,v1 ,v2 ,v3 ,v4 ,v5 : double );
363
transform :=@trans_affine_transform;
364
transform_2x2 :=@trans_affine_transform_2x2;
365
inverse_transform:=@trans_affine_inverse_transform;
370
constructor trans_affine.Construct(rect, parl : parallelo_ptr );
372
parl_to_parl(rect ,parl );
374
transform :=@trans_affine_transform;
375
transform_2x2 :=@trans_affine_transform_2x2;
376
inverse_transform:=@trans_affine_inverse_transform;
381
constructor trans_affine.Construct(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr );
383
rect_to_parl(x1 ,y1 ,x2 ,y2 ,parl );
385
transform :=@trans_affine_transform;
386
transform_2x2 :=@trans_affine_transform_2x2;
387
inverse_transform:=@trans_affine_inverse_transform;
392
constructor trans_affine.Construct(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double );
394
parl_to_rect(parl ,x1 ,y1 ,x2 ,y2 );
396
transform :=@trans_affine_transform;
397
transform_2x2 :=@trans_affine_transform_2x2;
398
inverse_transform:=@trans_affine_inverse_transform;
403
constructor trans_affine.Construct(tr : proc_transform );
413
transform_2x2 :=@trans_affine_transform_2x2;
414
inverse_transform:=@trans_affine_inverse_transform;
419
procedure trans_affine.parl_to_parl;
424
m0:=src[2 ] - src[0 ];
425
m1:=src[3 ] - src[1 ];
426
m2:=src[4 ] - src[0 ];
427
m3:=src[5 ] - src[1 ];
446
procedure trans_affine.rect_to_parl;
458
parl_to_parl(@src ,parl );
463
procedure trans_affine.parl_to_rect;
475
parl_to_parl(parl ,@dst );
480
procedure trans_affine.reset;
492
procedure trans_affine.multiply;
497
t0:=m0 * m.m0 + m1 * m.m2;
498
t2:=m2 * m.m0 + m3 * m.m2;
499
t4:=m4 * m.m0 + m5 * m.m2 + m.m4;
500
m1:=m0 * m.m1 + m1 * m.m3;
501
m3:=m2 * m.m1 + m3 * m.m3;
502
m5:=m4 * m.m1 + m5 * m.m3 + m.m5;
510
procedure trans_affine.premultiply;
524
procedure trans_affine.multiply_inv;
537
procedure trans_affine.premultiply_inv;
552
procedure trans_affine.invert;
564
t4:=-m4 * t0 - m5 * m2;
565
m5:=-m4 * m1 - m5 * m3;
573
procedure trans_affine.flip_x;
582
procedure trans_affine.flip_y;
591
procedure trans_affine.store_to;
603
procedure trans_affine.load_from;
615
function trans_affine.determinant;
618
result:=1 / (m0 * m3 - m1 * m2 );
628
function trans_affine.scale : double;
633
x:=0.707106781 * m0 + 0.707106781 * m2;
634
y:=0.707106781 * m1 + 0.707106781 * m3;
636
result:=Sqrt(x * x + y * y );
641
function trans_affine.is_identity;
644
is_equal_eps(m0 ,1 ,epsilon ) and
645
is_equal_eps(m1 ,0 ,epsilon ) and
646
is_equal_eps(m2 ,0 ,epsilon ) and
647
is_equal_eps(m3 ,1 ,epsilon ) and
648
is_equal_eps(m4 ,0 ,epsilon ) and
649
is_equal_eps(m5 ,0 ,epsilon );
654
function trans_affine.is_equal;
657
is_equal_eps(m0 ,m.m0 ,epsilon ) and
658
is_equal_eps(m1 ,m.m1 ,epsilon ) and
659
is_equal_eps(m2 ,m.m2 ,epsilon ) and
660
is_equal_eps(m3 ,m.m3 ,epsilon ) and
661
is_equal_eps(m4 ,m.m4 ,epsilon ) and
662
is_equal_eps(m5 ,m.m5 ,epsilon );
667
function trans_affine.rotation;
669
x1 ,y1 ,x2 ,y2 : double;
677
transform(@self ,@x1 ,@y1 );
678
transform(@self ,@x2 ,@y2 );
680
result:=ArcTan2(y2 - y1 ,x2 - x1 );
685
procedure trans_affine.translation;
690
transform(@self ,@dx ,@dy );
695
procedure trans_affine.scaling;
697
t : trans_affine_rotation;
699
x1 ,y1 ,x2 ,y2 : double;
707
trans_affine(t ):=self;
709
t.Construct(-rotation );
711
t.transform(@self ,@x1 ,@y1 );
712
t.transform(@self ,@x2 ,@y2 );
720
procedure trans_affine.scaling_abs;
722
sx^:=Sqrt(m0 * m0 + m2 * m2 );
723
sy^:=Sqrt(m1 * m1 + m3 * m3 );
728
procedure trans_affine.assign;
740
procedure trans_affine.assign_all;
749
transform :=@from.transform;
750
transform_2x2 :=@from.transform_2x2;
751
inverse_transform:=@from.inverse_transform;
756
function trans_affine.translate(x ,y : double ) : trans_affine_ptr;
766
function trans_affine.rotate(a : double ) : trans_affine_ptr;
768
ca ,sa ,t0 ,t2 ,t4 : double;
773
t0:=m0 * ca - m1 * sa;
774
t2:=m2 * ca - m3 * sa;
775
t4:=m4 * ca - m5 * sa;
777
m1:=m0 * sa + m1 * ca;
778
m3:=m2 * sa + m3 * ca;
779
m5:=m4 * sa + m5 * ca;
789
function trans_affine.scale(s : double ) : trans_affine_ptr;
803
function trans_affine.scale(x ,y : double ) : trans_affine_ptr;
817
constructor trans_affine_rotation.Construct;
819
inherited Construct(Cos(a ) ,Sin(a ) ,-Sin(a ) ,Cos(a ) ,0 ,0 );
824
constructor trans_affine_scaling.Construct(sx ,sy : double );
826
inherited Construct(sx ,0 ,0 ,sy ,0 ,0 );
831
constructor trans_affine_scaling.Construct(s : double );
833
inherited Construct(s ,0 ,0 ,s ,0 ,0 );
838
constructor trans_affine_translation.Construct;
840
inherited Construct(1 ,0 ,0 ,1 ,tx ,ty );
845
constructor trans_affine_skewing.Construct;
847
inherited Construct(1 ,Tan(sy ) ,Tan(sx ) ,1 ,0 ,0 );
852
constructor trans_affine_line_segment.Construct;
856
s : trans_affine_scaling;
857
r : trans_affine_rotation;
858
t : trans_affine_translation;
866
s.Construct(Sqrt(dx * dx + dy * dy ) / dist );
872
r.Construct(ArcTan2(dy ,dx ) );
876
t.Construct(x1 ,y1 );
883
constructor trans_affine_reflection_unit.Construct(ux ,uy : double );
886
2.0 * ux * ux - 1.0 ,
889
2.0 * uy * uy - 1.0 ,
895
constructor trans_affine_reflection.Construct(a : double );
897
inherited Construct(Cos(a ) ,Sin(a ) );
902
constructor trans_affine_reflection.Construct(x ,y : double );
916
nx:=x / Sqrt(x * x + y * y );
917
ny:=y / Sqrt(x * x + y * y );
921
inherited Construct(nx ,ny );