/* Libart_LGPL - library of basic graphic primitives * Copyright (C) 1998 Raph Levien * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* Simple manipulations with affine transformations */ #include #include /* for sprintf */ #include /* for strcpy */ #include "art_misc.h" #include "art_point.h" #include "art_affine.h" /* According to a strict interpretation of the libart structure, this routine should go into its own module, art_point_affine. However, it's only two lines of code, and it can be argued that it is one of the natural basic functions of an affine transformation. */ void art_affine_point (ArtPoint *dst, const ArtPoint *src, const double affine[6]) { double x, y; x = src->x; y = src->y; dst->x = x * affine[0] + y * affine[2] + affine[4]; dst->y = x * affine[1] + y * affine[3] + affine[5]; } void art_affine_invert (double dst[6], const double src[6]) { double r_det; r_det = 1.0 / (src[0] * src[3] - src[1] * src[2]); dst[0] = src[3] * r_det; dst[1] = -src[1] * r_det; dst[2] = -src[2] * r_det; dst[3] = src[0] * r_det; dst[4] = -src[4] * dst[0] - src[5] * dst[2]; dst[5] = -src[4] * dst[1] - src[5] * dst[3]; } /* flip the matrix, FALSE, FALSE is a simple copy operation, and TRUE, TRUE equals a rotation by 180 degrees */ void art_affine_flip (double dst_affine[6], const double src_affine[6], int horz, int vert) { dst_affine[0] = horz ? - src_affine[0] : src_affine[0]; dst_affine[1] = horz ? - src_affine[1] : src_affine[1]; dst_affine[2] = vert ? - src_affine[2] : src_affine[2]; dst_affine[3] = vert ? - src_affine[3] : src_affine[3]; dst_affine[4] = horz ? - src_affine[4] : src_affine[4]; dst_affine[5] = vert ? - src_affine[5] : src_affine[5]; } #define EPSILON 1e-6 /* It's ridiculous I have to write this myself. This is hardcoded to six digits of precision, which is good enough for PostScript. The return value is the number of characters (i.e. strlen (str)). It is no more than 12. */ static int art_ftoa (char str[80], double x) { char *p = str; int i, j; p = str; if (fabs (x) < EPSILON / 2) { strcpy (str, "0"); return 1; } if (x < 0) { *p++ = '-'; x = -x; } if ((int)floor ((x + EPSILON / 2) < 1)) { *p++ = '0'; *p++ = '.'; i = sprintf (p, "%06d", (int)floor ((x + EPSILON / 2) * 1e6)); while (i && p[i - 1] == '0') i--; if (i == 0) i--; p += i; } else if (x < 1e6) { i = sprintf (p, "%d", (int)floor (x + EPSILON / 2)); p += i; if (i < 6) { int ix; *p++ = '.'; x -= floor (x + EPSILON / 2); for (j = i; j < 6; j++) x *= 10; ix = floor (x + 0.5); for (j = 0; j < i; j++) ix *= 10; /* A cheap hack, this routine can round wrong for fractions near one. */ if (ix == 1000000) ix = 999999; sprintf (p, "%06d", ix); i = 6 - i; while (i && p[i - 1] == '0') i--; if (i == 0) i--; p += i; } } else p += sprintf (p, "%g", x); *p = '\0'; return p - str; } /* Convert affine trasformation to concise PostScript string representation. The identity transform is mapped to the null string. */ #include void art_affine_to_string (char str[128], const double src[6]) { char tmp[80]; int i, ix; #if 0 for (i = 0; i < 1000; i++) { double d = rand () * .1 / RAND_MAX; art_ftoa (tmp, d); printf ("%g %f %s\n", d, d, tmp); } #endif if (fabs (src[4]) < EPSILON && fabs (src[5]) < EPSILON) { /* could be scale or rotate */ if (fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) { /* scale */ if (fabs (src[0] - 1) < EPSILON && fabs (src[3] - 1) < EPSILON) { /* identity transform */ str[0] = '\0'; return; } else { sprintf (str, "%g %g scale", src[0], src[3]); return; } } else { /* could be rotate */ if (fabs (src[0] - src[3]) < EPSILON && fabs (src[1] + src[2]) < EPSILON && (src[0] * src[0] + src[1] * src[1] - 1) < 2 * EPSILON) { double theta; theta = (180 / M_PI) * atan2 (src[1], src[0]); art_ftoa (tmp, theta); sprintf (str, "%s rotate", tmp); return; } } } else { /* could be translate */ if (fabs (src[0] - 1) < EPSILON && fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON && fabs (src[3] - 1) < EPSILON) { sprintf (str, "%g %g translate", src[4], src[5]); return; } } ix = 0; str[ix++] = '['; str[ix++] = ' '; for (i = 0; i < 6; i++) { ix += art_ftoa (str + ix, src[i]); str[ix++] = ' '; } strcpy (str + ix, "] concat"); } /* dst is the composition of doing affine src1 then src2. Note that the PostScript concat operator multiplies on the left, i.e. if "M concat" is equivalent to "CTM = multiply (M, CTM)"; It is safe to call this function with dst equal to one of the args. */ void art_affine_multiply (double dst[6], const double src1[6], const double src2[6]) { double d0, d1, d2, d3, d4, d5; d0 = src1[0] * src2[0] + src1[1] * src2[2]; d1 = src1[0] * src2[1] + src1[1] * src2[3]; d2 = src1[2] * src2[0] + src1[3] * src2[2]; d3 = src1[2] * src2[1] + src1[3] * src2[3]; d4 = src1[4] * src2[0] + src1[5] * src2[2] + src2[4]; d5 = src1[4] * src2[1] + src1[5] * src2[3] + src2[5]; dst[0] = d0; dst[1] = d1; dst[2] = d2; dst[3] = d3; dst[4] = d4; dst[5] = d5; } /* set up the identity matrix */ void art_affine_identity (double dst[6]) { dst[0] = 1; dst[1] = 0; dst[2] = 0; dst[3] = 1; dst[4] = 0; dst[5] = 0; } /* set up a scaling matrix */ void art_affine_scale (double dst[6], double sx, double sy) { dst[0] = sx; dst[1] = 0; dst[2] = 0; dst[3] = sy; dst[4] = 0; dst[5] = 0; } /* set up a rotation matrix; theta is given in degrees */ void art_affine_rotate (double dst[6], double theta) { double s, c; s = sin (theta * M_PI / 180.0); c = cos (theta * M_PI / 180.0); dst[0] = c; dst[1] = s; dst[2] = -s; dst[3] = c; dst[4] = 0; dst[5] = 0; } /* set up a shearing matrix; theta is given in degrees */ void art_affine_shear (double dst[6], double theta) { double t; t = tan (theta * M_PI / 180.0); dst[0] = 1; dst[1] = 0; dst[2] = t; dst[3] = 1; dst[4] = 0; dst[5] = 0; } /* set up a translation matrix */ void art_affine_translate (double dst[6], double tx, double ty) { dst[0] = 1; dst[1] = 0; dst[2] = 0; dst[3] = 1; dst[4] = tx; dst[5] = ty; } /* find the affine's "expansion factor", i.e. the scale amount */ double art_affine_expansion (const double src[6]) { return sqrt (src[0] * src[3] - src[1] * src[2]); } /* Determine whether the affine transformation is rectilinear, i.e. whether a rectangle aligned to the grid is transformed into another rectangle aligned to the grid. */ int art_affine_rectilinear (const double src[6]) { return ((fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) || (fabs (src[0]) < EPSILON && fabs (src[3]) < EPSILON)); } /* Determine whether two affine transformations are equal within grid allignment */ int art_affine_equal (double matrix1[6], double matrix2[6]) { return (fabs (matrix1[0] - matrix2[0]) < EPSILON && fabs (matrix1[1] - matrix2[1]) < EPSILON && fabs (matrix1[2] - matrix2[2]) < EPSILON && fabs (matrix1[3] - matrix2[3]) < EPSILON && fabs (matrix1[4] - matrix2[4]) < EPSILON && fabs (matrix1[5] - matrix2[5]) < EPSILON); }