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