1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.4 (Public License)
3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
4 //
5 // Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3)
6 // Pascal Port By: Milan Marusinec alias Milano
7 //                 milan@marusinec.sk
8 //                 http://www.aggpas.org
9 // Copyright (c) 2005-2006
10 //
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.
15 //
16 //----------------------------------------------------------------------------
17 // Contact: mcseem@antigrain.com
18 //          mcseemagg@yahoo.com
19 //          http://www.antigrain.com
20 //
21 //----------------------------------------------------------------------------
22 //
23 // Affine transformation classes.
24 //
25 // [Pascal Port History] -----------------------------------------------------
26 //
27 // 26.10.2007-Milano: Reflection Transformations
28 // 27.09.2005-Milano: Complete unit port
29 //
30 //----------------------------------------------------------------------------
31 //
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.
37 //
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:
42 //
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,
49 //       * scale X to 2.0,
50 //       * scale Y to 1.5,
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:
56 //
57 //    affine_matrix m;
58 //    m *= rotate_matrix(30);
59 //    m *= scaleX_matrix(2.0);
60 //    m *= scaleY_matrix(1.5);
61 //    m *= move_matrix(100,100);
62 //
63 //    m.transform_my_point_at_last(x, y);
64 //
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.
68 //
69 // So, how to use it? Very easy - literally as it's shown above. Not quite,
70 // let us write a correct example:
71 //
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);
77 //
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):
81 //
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)
85 //
86 { agg_trans_affine.pas }
87 unit
88  agg_trans_affine ;
89 
90 INTERFACE
91 
92 {$I agg_mode.inc }
93 
94 uses
95  Math ,
96  agg_basics ;
97 
98 { TYPES DEFINITION }
99 const
100  affine_epsilon = 1e-14; // About of precision of doubles
101 
102 type
103  trans_affine_ptr = ^trans_affine;
104 
105  proc_transform = procedure(this : trans_affine_ptr; x ,y : double_ptr );
106 
107  parallelo_ptr = ^parallelogram;
108  parallelogram = array[0..5 ] of double;
109 
110  trans_affine = object
111  { sx ,shy ,shx ,sy ,tx ,ty }
112    m0 ,m1  ,m2  ,m3 ,m4 ,m5 : double;
113 
114    transform         ,
115    transform_2x2     ,
116    inverse_transform : proc_transform;
117 
118   // Construct an identity matrix - it does not transform anything
119    constructor Construct; overload;
120 
121   // Construct a custom matrix. Usually used in derived classes
122    constructor Construct(v0 ,v1 ,v2 ,v3 ,v4 ,v5 : double ); overload;
123 
124   // Construct a matrix to transform a parallelogram to another one
125    constructor Construct(rect, parl : parallelo_ptr ); overload;
126 
127   // Construct a matrix to transform a rectangle to a parallelogram
128    constructor Construct(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr ); overload;
129 
130   // Construct a matrix to transform a parallelogram to a rectangle
131    constructor Construct(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double ); overload;
132 
133   // Construct a matrix with different transform function
134    constructor Construct(tr : proc_transform ); overload;
135 
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 );
146 
147   //------------------------------------------ Operations
148   // Reset - actually load an identity matrix
149    procedure reset; virtual;
150 
151   // Multiply matrix to another one
152    procedure multiply(m : trans_affine_ptr );
153 
154   // Multiply "m" to "this" and assign the result to "this"
155    procedure premultiply(m : trans_affine_ptr );
156 
157   // Multiply matrix to inverse of another one
158    procedure multiply_inv(m : trans_affine_ptr );
159 
160   // Multiply inverse of "m" to "this" and assign the result to "this"
161    procedure premultiply_inv(m : trans_affine_ptr );
162 
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.
166    procedure invert;
167 
168   // Mirroring around X
169    procedure flip_x;
170 
171   // Mirroring around Y
172    procedure flip_y;
173 
174   //------------------------------------------- Load/Store
175   // Store matrix to an array [6] of double
176    procedure store_to(m : parallelo_ptr );
177 
178   // Load matrix from an array [6] of double
179    procedure load_from(m : parallelo_ptr );
180 
181   //-------------------------------------------- Transformations
182   // Direct transformation x and y
183   // see: transform : proc_transform; above
184 
185   // Direct transformation x and y, 2x2 matrix only, no translation
186   // procedure transform_2x2(x ,y : double_ptr );
187 
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 );
192 
193   //-------------------------------------------- Auxiliary
194   // Calculate the determinant of matrix
195    function  determinant : double;
196 
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;
201 
202   // Check to see if it's an identity matrix
is_identitynull203    function  is_identity(epsilon : double = affine_epsilon ) : boolean;
204 
205   // Check to see if two matrices are equal
is_equalnull206    function  is_equal(m : trans_affine; epsilon : double = affine_epsilon ) : boolean;
207 
208   // Determine the major parameters. Use carefully considering degenerate matrices
rotationnull209    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 );
213 
214   // Trans Affine Assignations
215    procedure assign    (from : trans_affine_ptr );
216    procedure assign_all(from : trans_affine_ptr );
217 
218   // Direct transformations operations
translatenull219    function  translate(x ,y : double ) : trans_affine_ptr;
rotatenull220    function  rotate   (a : double ) : trans_affine_ptr;
scalenull221    function  scale    (s : double ) : trans_affine_ptr; overload;
scalenull222    function  scale    (x ,y : double ) : trans_affine_ptr; overload;
223 
224   end;
225 
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
230 // often.
231  trans_affine_rotation = object(trans_affine )
232    constructor Construct(a : double );
233 
234   end;
235 
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;
241 
242   end;
243 
244 //================================================trans_affine_translation
245 // Translation matrix
246  trans_affine_translation = object(trans_affine )
247    constructor Construct(tx ,ty : double );
248 
249   end;
250 
251 //====================================================trans_affine_skewing
252 // Sckewing (shear) matrix
253  trans_affine_skewing = object(trans_affine )
254    constructor Construct(sx ,sy : double );
255 
256   end;
257 
258 //===============================================trans_affine_line_segment
259 // Rotate, Scale and Translate, associating 0...dist with line segment
260 // x1,y1,x2,y2
261  trans_affine_line_segment = object(trans_affine )
262    constructor Construct(x1 ,y1 ,x2 ,y2 ,dist : double );
263 
264   end;
265 
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 );
272 
273   end;
274 
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;
282 
283   end;
284 
285 { GLOBAL PROCEDURES }
286  function  is_equal_eps(v1 ,v2 ,epsilon : double ) : boolean;
287 
288 
289 IMPLEMENTATION
290 { UNIT IMPLEMENTATION }
291 { is_equal_eps }
292 function is_equal_eps;
293 begin
294  result:=Abs(v1 - v2 ) < epsilon;
295 
296 end;
297 
298 { trans_affine_transform }
299 procedure trans_affine_transform(this : trans_affine_ptr; x ,y : double_ptr );
300 var
301  tx : double;
302 
303 begin
304  tx:=x^;
305  x^:=tx * this.m0 + y^ * this.m2 + this.m4;
306  y^:=tx * this.m1 + y^ * this.m3 + this.m5;
307 
308 end;
309 
310 { trans_affine_transform_2x2 }
311 procedure trans_affine_transform_2x2(this : trans_affine_ptr; x ,y : double_ptr );
312 var
313  tx : double;
314 
315 begin
316  tx:=x^;
317  x^:=tx * this.m0 + y^ * this.m2;
318  y^:=tx * this.m1 + y^ * this.m3;
319 
320 end;
321 
322 { trans_affine_inverse_transform }
323 procedure trans_affine_inverse_transform(this : trans_affine_ptr; x ,y : double_ptr );
324 var
325  d ,a ,b : double;
326 
327 begin
328  d:=this.determinant;
329  a:=(x^ - this.m4 ) * d;
330  b:=(y^ - this.m5 ) * d;
331 
332  x^:=a * this.m3 - b * this.m2;
333  y^:=b * this.m0 - a * this.m1;
334 
335 end;
336 
337 { CONSTRUCT }
338 constructor trans_affine.Construct;
339 begin
340  m0:=1;
341  m1:=0;
342  m2:=0;
343  m3:=1;
344  m4:=0;
345  m5:=0;
346 
347  transform        :=@trans_affine_transform;
348  transform_2x2    :=@trans_affine_transform_2x2;
349  inverse_transform:=@trans_affine_inverse_transform;
350 
351 end;
352 
353 { CONSTRUCT }
354 constructor trans_affine.Construct(v0 ,v1 ,v2 ,v3 ,v4 ,v5 : double );
355 begin
356  m0:=v0;
357  m1:=v1;
358  m2:=v2;
359  m3:=v3;
360  m4:=v4;
361  m5:=v5;
362 
363  transform        :=@trans_affine_transform;
364  transform_2x2    :=@trans_affine_transform_2x2;
365  inverse_transform:=@trans_affine_inverse_transform;
366 
367 end;
368 
369 { CONSTRUCT }
370 constructor trans_affine.Construct(rect, parl : parallelo_ptr );
371 begin
372  parl_to_parl(rect ,parl );
373 
374  transform        :=@trans_affine_transform;
375  transform_2x2    :=@trans_affine_transform_2x2;
376  inverse_transform:=@trans_affine_inverse_transform;
377 
378 end;
379 
380 { CONSTRUCT }
381 constructor trans_affine.Construct(x1 ,y1 ,x2 ,y2 : double; parl : parallelo_ptr );
382 begin
383  rect_to_parl(x1 ,y1 ,x2 ,y2 ,parl );
384 
385  transform        :=@trans_affine_transform;
386  transform_2x2    :=@trans_affine_transform_2x2;
387  inverse_transform:=@trans_affine_inverse_transform;
388 
389 end;
390 
391 { CONSTRUCT }
392 constructor trans_affine.Construct(parl : parallelo_ptr; x1 ,y1 ,x2 ,y2 : double );
393 begin
394  parl_to_rect(parl ,x1 ,y1 ,x2 ,y2 );
395 
396  transform        :=@trans_affine_transform;
397  transform_2x2    :=@trans_affine_transform_2x2;
398  inverse_transform:=@trans_affine_inverse_transform;
399 
400 end;
401 
402 { CONSTRUCT }
403 constructor trans_affine.Construct(tr : proc_transform );
404 begin
405  m0:=1;
406  m1:=0;
407  m2:=0;
408  m3:=1;
409  m4:=0;
410  m5:=0;
411 
412  transform        :=tr;
413  transform_2x2    :=@trans_affine_transform_2x2;
414  inverse_transform:=@trans_affine_inverse_transform;
415 
416 end;
417 
418 { parl_to_parl }
419 procedure trans_affine.parl_to_parl;
420 var
421  m : trans_affine;
422 
423 begin
424  m0:=src[2 ] - src[0 ];
425  m1:=src[3 ] - src[1 ];
426  m2:=src[4 ] - src[0 ];
427  m3:=src[5 ] - src[1 ];
428  m4:=src[0 ];
429  m5:=src[1 ];
430 
431  invert;
432 
433  m.Construct(
434   dst[2 ] - dst[0 ] ,
435   dst[3 ] - dst[1 ] ,
436   dst[4 ] - dst[0 ] ,
437   dst[5 ] - dst[1 ] ,
438   dst[0 ] ,
439   dst[1 ] );
440 
441  multiply(@m );
442 
443 end;
444 
445 { rect_to_parl }
446 procedure trans_affine.rect_to_parl;
447 var
448  src : parallelogram;
449 
450 begin
451  src[0 ]:=x1;
452  src[1 ]:=y1;
453  src[2 ]:=x2;
454  src[3 ]:=y1;
455  src[4 ]:=x2;
456  src[5 ]:=y2;
457 
458  parl_to_parl(@src ,parl );
459 
460 end;
461 
462 { parl_to_rect }
463 procedure trans_affine.parl_to_rect;
464 var
465  dst : parallelogram;
466 
467 begin
468  dst[0 ]:=x1;
469  dst[1 ]:=y1;
470  dst[2 ]:=x2;
471  dst[3 ]:=y1;
472  dst[4 ]:=x2;
473  dst[5 ]:=y2;
474 
475  parl_to_parl(parl ,@dst );
476 
477 end;
478 
479 { reset }
480 procedure trans_affine.reset;
481 begin
482  m0:=1;
483  m1:=0;
484  m2:=0;
485  m3:=1;
486  m4:=0;
487  m5:=0;
488 
489 end;
490 
491 { multiply }
492 procedure trans_affine.multiply;
493 var
494  t0 ,t2 ,t4 : double;
495 
496 begin
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;
503  m0:=t0;
504  m2:=t2;
505  m4:=t4;
506 
507 end;
508 
509 { premultiply }
510 procedure trans_affine.premultiply;
511 var
512  t : trans_affine;
513 
514 begin
515  t.assign_all(m );
516 
517  t.multiply(@self );
518 
519  assign(@t );
520 
521 end;
522 
523 { multiply_inv }
524 procedure trans_affine.multiply_inv;
525 var
526  t : trans_affine;
527 
528 begin
529  t.assign_all(m );
530  t.invert;
531 
532  multiply(@t );
533 
534 end;
535 
536 { premultiply_inv }
537 procedure trans_affine.premultiply_inv;
538 var
539  t : trans_affine;
540 
541 begin
542  t.assign_all(m );
543 
544  t.invert;
545  t.multiply(@self );
546 
547  assign(@t );
548 
549 end;
550 
551 { invert }
552 procedure trans_affine.invert;
553 var
554  d ,t0 ,t4 : double;
555 
556 begin
557  d:=determinant;
558 
559  t0:= m3 * d;
560  m3:= m0 * d;
561  m1:=-m1 * d;
562  m2:=-m2 * d;
563 
564  t4:=-m4 * t0 - m5 * m2;
565  m5:=-m4 * m1 - m5 * m3;
566 
567  m0:=t0;
568  m4:=t4;
569 
570 end;
571 
572 { flip_x }
573 procedure trans_affine.flip_x;
574 begin
575  m0:=-m0;
576  m1:=-m1;
577  m4:=-m4;
578 
579 end;
580 
581 { flip_y }
582 procedure trans_affine.flip_y;
583 begin
584  m2:=-m2;
585  m3:=-m3;
586  m5:=-m5;
587 
588 end;
589 
590 { store_to }
591 procedure trans_affine.store_to;
592 begin
593  m[0 ]:=m0;
594  m[1 ]:=m1;
595  m[2 ]:=m2;
596  m[3 ]:=m3;
597  m[4 ]:=m4;
598  m[5 ]:=m5;
599 
600 end;
601 
602 { load_from }
603 procedure trans_affine.load_from;
604 begin
605  m0:=m[0 ];
606  m1:=m[1 ];
607  m2:=m[2 ];
608  m3:=m[3 ];
609  m4:=m[4 ];
610  m5:=m[5 ];
611 
612 end;
613 
614 { determinant }
615 function trans_affine.determinant;
616 begin
617  try
618   result:=1 / (m0 * m3 - m1 * m2 );
619 
620  except
621   result:=0;
622 
623  end;
624 
625 end;
626 
627 { scale }
628 function trans_affine.scale : double;
629 var
630  x ,y : double;
631 
632 begin
633  x:=0.707106781 * m0 + 0.707106781 * m2;
634  y:=0.707106781 * m1 + 0.707106781 * m3;
635 
636  result:=Sqrt(x * x + y * y );
637 
638 end;
639 
640 { is_identity }
641 function trans_affine.is_identity;
642 begin
643  result:=
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 );
650 
651 end;
652 
653 { is_equal }
654 function trans_affine.is_equal;
655 begin
656  result:=
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 );
663 
664 end;
665 
666 { rotation }
667 function trans_affine.rotation;
668 var
669  x1 ,y1 ,x2 ,y2 : double;
670 
671 begin
672  x1:=0;
673  y1:=0;
674  x2:=1;
675  y2:=0;
676 
677  transform(@self ,@x1 ,@y1 );
678  transform(@self ,@x2 ,@y2 );
679 
680  result:=ArcTan2(y2 - y1 ,x2 - x1 );
681 
682 end;
683 
684 { translation }
685 procedure trans_affine.translation;
686 begin
687  dx:=0;
688  dy:=0;
689 
690  transform(@self ,@dx ,@dy );
691 
692 end;
693 
694 { scaling }
695 procedure trans_affine.scaling;
696 var
697  t : trans_affine_rotation;
698 
699  x1 ,y1 ,x2 ,y2 : double;
700 
701 begin
702  x1:=0;
703  y1:=0;
704  x2:=1;
705  y2:=1;
706 
707  trans_affine(t ):=self;
708 
709  t.Construct(-rotation );
710 
711  t.transform(@self ,@x1 ,@y1 );
712  t.transform(@self ,@x2 ,@y2 );
713 
714  sx^:=x2 - x1;
715  sy^:=y2 - y1;
716 
717 end;
718 
719 { scaling_abs }
720 procedure trans_affine.scaling_abs;
721 begin
722  sx^:=Sqrt(m0 * m0 + m2 * m2 );
723  sy^:=Sqrt(m1 * m1 + m3 * m3 );
724 
725 end;
726 
727 { ASSIGN }
728 procedure trans_affine.assign;
729 begin
730  m0:=from.m0;
731  m1:=from.m1;
732  m2:=from.m2;
733  m3:=from.m3;
734  m4:=from.m4;
735  m5:=from.m5;
736 
737 end;
738 
739 { ASSIGN_ALL }
740 procedure trans_affine.assign_all;
741 begin
742  m0:=from.m0;
743  m1:=from.m1;
744  m2:=from.m2;
745  m3:=from.m3;
746  m4:=from.m4;
747  m5:=from.m5;
748 
749  transform        :=@from.transform;
750  transform_2x2    :=@from.transform_2x2;
751  inverse_transform:=@from.inverse_transform;
752 
753 end;
754 
755 { TRANSLATE }
756 function trans_affine.translate(x ,y : double ) : trans_affine_ptr;
757 begin
758  m4:=m4 + x;
759  m5:=m5 + y;
760 
761  result:=@self;
762 
763 end;
764 
765 { ROTATE }
766 function trans_affine.rotate(a : double ) : trans_affine_ptr;
767 var
768  ca ,sa ,t0 ,t2 ,t4 : double;
769 
770 begin
771  ca:=Cos(a);
772  sa:=Sin(a);
773  t0:=m0 * ca - m1 * sa;
774  t2:=m2 * ca - m3 * sa;
775  t4:=m4 * ca - m5 * sa;
776 
777  m1:=m0 * sa + m1 * ca;
778  m3:=m2 * sa + m3 * ca;
779  m5:=m4 * sa + m5 * ca;
780  m0:=t0;
781  m2:=t2;
782  m4:=t4;
783 
784  result:=@self;
785 
786 end;
787 
788 { SCALE }
789 function trans_affine.scale(s : double ) : trans_affine_ptr;
790 begin
791  m0:=m0 * s;
792  m1:=m1 * s;
793  m2:=m2 * s;
794  m3:=m3 * s;
795  m4:=m4 * s;
796  m5:=m5 * s;
797 
798  result:=@self;
799 
800 end;
801 
802 { SCALE }
803 function trans_affine.scale(x ,y : double ) : trans_affine_ptr;
804 begin
805  m0:=m0 * x;
806  m2:=m2 * x;
807  m4:=m4 * x;
808  m1:=m1 * y;
809  m3:=m3 * y;
810  m5:=m5 * y;
811 
812  result:=@self;
813 
814 end;
815 
816 { CONSTRUCT }
817 constructor trans_affine_rotation.Construct;
818 begin
819  inherited Construct(Cos(a ) ,Sin(a ) ,-Sin(a ) ,Cos(a ) ,0 ,0 );
820 
821 end;
822 
823 { CONSTRUCT }
824 constructor trans_affine_scaling.Construct(sx ,sy : double );
825 begin
826  inherited Construct(sx ,0 ,0 ,sy ,0 ,0 );
827 
828 end;
829 
830 { CONSTRUCT }
831 constructor trans_affine_scaling.Construct(s : double );
832 begin
833  inherited Construct(s ,0 ,0 ,s ,0 ,0 );
834 
835 end;
836 
837 { CONSTRUCT }
838 constructor trans_affine_translation.Construct;
839 begin
840  inherited Construct(1 ,0 ,0 ,1 ,tx ,ty );
841 
842 end;
843 
844 { CONSTRUCT }
845 constructor trans_affine_skewing.Construct;
846 begin
847  inherited Construct(1 ,Tan(sy ) ,Tan(sx ) ,1 ,0 ,0 );
848 
849 end;
850 
851 { CONSTRUCT }
852 constructor trans_affine_line_segment.Construct;
853 var
854  dx ,dy : double;
855 
856  s : trans_affine_scaling;
857  r : trans_affine_rotation;
858  t : trans_affine_translation;
859 
860 begin
861  dx:=x2 - x1;
862  dy:=y2 - y1;
863 
864  if dist > 0 then
865   begin
866    s.Construct(Sqrt(dx * dx + dy * dy ) / dist );
867 
868    multiply(@s );
869 
870   end;
871 
872  r.Construct(ArcTan2(dy ,dx ) );
873 
874  multiply(@r );
875 
876  t.Construct(x1 ,y1 );
877 
878  multiply(@t );
879 
880 end;
881 
882 { CONSTRUCT }
883 constructor trans_affine_reflection_unit.Construct(ux ,uy : double );
884 begin
885  inherited Construct(
886   2.0 * ux * ux - 1.0 ,
887   2.0 * ux * uy ,
888   2.0 * ux * uy ,
889   2.0 * uy * uy - 1.0 ,
890   0.0 ,0.0 );
891 
892 end;
893 
894 { CONSTRUCT }
895 constructor trans_affine_reflection.Construct(a : double );
896 begin
897  inherited Construct(Cos(a ) ,Sin(a ) );
898 
899 end;
900 
901 { CONSTRUCT }
902 constructor trans_affine_reflection.Construct(x ,y : double );
903 var
904  nx ,ny : double;
905 
906 begin
907  if (x = 0 ) and
908     (y = 0 ) then
909   begin
910    x:=0;
911    y:=0;
912 
913   end
914  else
915   begin
916    nx:=x / Sqrt(x * x + y * y );
917    ny:=y / Sqrt(x * x + y * y );
918 
919   end;
920 
921  inherited Construct(nx ,ny );
922 
923 end;
924 
925 END.
926 
927