386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gsmatrix.c
CommitLineData
e4d7ed6d
WJ
1/* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gsmatrix.c */
21/* Matrix operators for GhostScript library */
22#include "math_.h"
23#include "gx.h"
24#include "gserrors.h"
25#include "gxfixed.h"
26#include "gxarith.h"
27#include "gxmatrix.h"
28
29/* The identity matrix */
30/* This should be private (static), but the interpreter */
31/* has to be able to fill in the "unused" words. */
32/*static*/ gs_matrix gs_identity_matrix =
33 { identity_matrix_body };
34
35/* ------ Matrix creation ------ */
36
37/* Create an identity matrix */
38void
39gs_make_identity(gs_matrix *pmat)
40{ *pmat = gs_identity_matrix;
41}
42
43/* Create a translation matrix */
44int
45gs_make_translation(floatp dx, floatp dy, register gs_matrix *pmat)
46{ *pmat = gs_identity_matrix;
47 pmat->tx = dx;
48 pmat->ty = dy;
49 return 0;
50}
51
52/* Create a scaling matrix */
53int
54gs_make_scaling(floatp sx, floatp sy, register gs_matrix *pmat)
55{ *pmat = gs_identity_matrix;
56 pmat->xx = sx;
57 pmat->yy = sy;
58 return 0;
59}
60
61/* Create a rotation matrix. */
62/* The angle is in degrees. */
63int
64gs_make_rotation(floatp ang, register gs_matrix *pmat)
65{ float theta = ang * (M_PI / 180.0);
66 *pmat = gs_identity_matrix;
67 pmat->xx = pmat->yy = cos(theta);
68 pmat->yx = -(pmat->xy = sin(theta));
69 return 0;
70}
71
72/* ------ Matrix arithmetic ------ */
73
74/* Multiply two matrices. We should check for floating exceptions, */
75/* but for the moment it's just too awkward. */
76/* Since this is used heavily, we check for shortcuts. */
77int
78gs_matrix_multiply(const gs_matrix *pm1, const gs_matrix *pm2, register gs_matrix *pmr)
79{ float xx1 = pm1->xx, yy1 = pm1->yy;
80 float tx1 = pm1->tx, ty1 = pm1->ty;
81 float xx2 = pm2->xx, yy2 = pm2->yy;
82 float xy2 = pm2->xy, yx2 = pm2->yx;
83 if ( !is_skewed(pm1) )
84 { pmr->tx = tx1 * xx2 + pm2->tx;
85 pmr->ty = ty1 * yy2 + pm2->ty;
86 if ( is_fzero(xy2) )
87 pmr->xy = 0;
88 else
89 pmr->xy = xx1 * xy2,
90 pmr->ty += tx1 * xy2;
91 pmr->xx = xx1 * xx2;
92 if ( is_fzero(yx2) )
93 pmr->yx = 0;
94 else
95 pmr->yx = yy1 * yx2,
96 pmr->tx += ty1 * yx2;
97 pmr->yy = yy1 * yy2;
98 }
99 else
100 { pmr->xx = xx1 * xx2 + pm1->xy * yx2;
101 pmr->xy = xx1 * xy2 + pm1->xy * yy2;
102 pmr->yy = pm1->yx * xy2 + yy1 * yy2;
103 pmr->yx = pm1->yx * xx2 + yy1 * yx2;
104 pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx;
105 pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty;
106 }
107 return 0;
108}
109
110/* Invert a matrix. Return gs_error_undefinedresult if not invertible. */
111int
112gs_matrix_invert(register const gs_matrix *pm, register gs_matrix *pmr)
113{ /* We have to be careful about fetch/store order, */
114 /* because pm might be the same as pmr. */
115 if ( !is_skewed(pm) )
116 { if ( is_fzero(pm->xx) || is_fzero(pm->yy) )
117 return_error(gs_error_undefinedresult);
118 pmr->tx = - (pmr->xx = 1.0 / pm->xx) * pm->tx;
119 pmr->xy = 0.0;
120 pmr->yx = 0.0;
121 pmr->ty = - (pmr->yy = 1.0 / pm->yy) * pm->ty;
122 }
123 else
124 { double det = pm->xx * pm->yy - pm->xy * pm->yx;
125 float mxx = pm->xx, mtx = pm->tx;
126 if ( det == 0 ) return_error(gs_error_undefinedresult);
127 pmr->xx = pm->yy / det;
128 pmr->xy = - pm->xy / det;
129 pmr->yx = - pm->yx / det;
130 pmr->yy = mxx / det; /* xx is already changed */
131 pmr->tx = - (mtx * pmr->xx + pm->ty * pmr->yx);
132 pmr->ty = - (mtx * pmr->xy + pm->ty * pmr->yy); /* tx is already changed */
133 }
134 return 0;
135}
136
137/* Rotate a matrix, possibly in place. The angle is in degrees. */
138int
139gs_matrix_rotate(register const gs_matrix *pm, floatp ang, register gs_matrix *pmr)
140{ float mxx, mxy;
141 int quads;
142 float tsin, tcos;
143 /* We do some special checking for multiples of 90, */
144 /* so we don't get any rounding errors. */
145 if ( ang >= -360 && ang <= 360 &&
146 ang == (quads = (int)ang / 90) * 90
147 )
148 { int isin = 0, icos = 1, t;
149 quads &= 3;
150 while ( quads-- ) t = isin, isin = icos, icos = -t;
151 tsin = isin, tcos = icos;
152 }
153 else
154 { float theta = ang * (M_PI / 180.0);
155 tsin = sin(theta);
156 tcos = cos(theta);
157 }
158 mxx = pm->xx, mxy = pm->xy;
159 pmr->xx = tcos * mxx + tsin * pm->yx;
160 pmr->xy = tcos * mxy + tsin * pm->yy;
161 pmr->yx = tcos * pm->yx - tsin * mxx;
162 pmr->yy = tcos * pm->yy - tsin * mxy;
163 pmr->tx = pm->tx;
164 pmr->ty = pm->ty;
165 return 0;
166}
167
168/* ------ Coordinate transformations (floating point) ------ */
169
170/* Note that all the transformation routines take separate */
171/* x and y arguments, but return their result in a point. */
172
173/* Transform a point. */
174int
175gs_point_transform(floatp x, floatp y, register const gs_matrix *pmat,
176 register gs_point *ppt)
177{ ppt->x = x * pmat->xx + pmat->tx;
178 ppt->y = y * pmat->yy + pmat->ty;
179 if ( !is_fzero(pmat->yx) )
180 ppt->x += y * pmat->yx;
181 if ( !is_fzero(pmat->xy) )
182 ppt->y += x * pmat->xy;
183 return 0;
184}
185
186/* Inverse-transform a point. */
187/* Return gs_error_undefinedresult if the matrix is not invertible. */
188int
189gs_point_transform_inverse(floatp x, floatp y, register const gs_matrix *pmat,
190 register gs_point *ppt)
191{ if ( !is_skewed(pmat) )
192 { if ( is_fzero(pmat->xx) || is_fzero(pmat->yy) )
193 return_error(gs_error_undefinedresult);
194 ppt->x = (x - pmat->tx) / pmat->xx;
195 ppt->y = (y - pmat->ty) / pmat->yy;
196 return 0;
197 }
198 else
199 { /* There are faster ways to do this, */
200 /* but we won't implement one unless we have to. */
201 gs_matrix imat;
202 int code = gs_matrix_invert(pmat, &imat);
203 if ( code < 0 ) return code;
204 return gs_point_transform(x, y, &imat, ppt);
205 }
206}
207
208/* Transform a distance. */
209int
210gs_distance_transform(floatp dx, floatp dy, register const gs_matrix *pmat,
211 register gs_point *pdpt)
212{ pdpt->x = dx * pmat->xx;
213 pdpt->y = dy * pmat->yy;
214 if ( !is_fzero(pmat->yx) )
215 pdpt->x += dy * pmat->yx;
216 if ( !is_fzero(pmat->xy) )
217 pdpt->y += dx * pmat->xy;
218 return 0;
219}
220
221/* Inverse-transform a distance. */
222/* Return gs_error_undefinedresult if the matrix is not invertible. */
223int
224gs_distance_transform_inverse(floatp dx, floatp dy,
225 register const gs_matrix *pmat, register gs_point *pdpt)
226{ if ( !is_skewed(pmat) )
227 { if ( is_fzero(pmat->xx) || is_fzero(pmat->yy) )
228 return_error(gs_error_undefinedresult);
229 pdpt->x = dx / pmat->xx;
230 pdpt->y = dy / pmat->yy;
231 }
232 else
233 { double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx;
234 if ( det == 0 ) return_error(gs_error_undefinedresult);
235 pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det;
236 pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det;
237 }
238 return 0;
239}
240
241/* Inverse-transform a bounding box. */
242/* Return gs_error_undefinedresult if the matrix is not invertible. */
243int
244gs_bbox_transform_inverse(gs_rect *pbox_in, gs_matrix *pmat,
245 gs_rect *pbox_out)
246{ int code;
247 gs_point p, w, h;
248 double xmin, ymin, xmax, ymax; /* gs_point uses double */
249 /* We must recompute the max and min after transforming, */
250 /* since a rotation may be involved. */
251 if ( (code = gs_point_transform_inverse(pbox_in->p.x, pbox_in->p.y, pmat, &p)) < 0 ||
252 (code = gs_distance_transform_inverse(pbox_in->q.x - pbox_in->p.x, 0.0, pmat, &w)) < 0 ||
253 (code = gs_distance_transform_inverse(0.0, pbox_in->q.y - pbox_in->p.y, pmat, &h)) < 0
254 )
255 return code;
256 xmin = xmax = p.x;
257 if ( w.x < 0 ) xmin += w.x;
258 else xmax += w.x;
259 if ( h.x < 0 ) xmin += h.x;
260 else xmax += h.x;
261 ymin = ymax = p.y;
262 if ( w.y < 0 ) ymin += w.y;
263 else ymax += w.y;
264 if ( h.y < 0 ) ymin += h.y;
265 else ymax += h.y;
266 pbox_out->p.x = xmin, pbox_out->p.y = ymin;
267 pbox_out->q.x = xmax, pbox_out->q.y = ymax;
268 return 0;
269}
270
271/* ------ Coordinate transformations (to fixed point) ------ */
272
273/* Transform a point with a fixed-point result. */
274int
275gs_point_transform2fixed(register gs_matrix_fixed *pmat,
276 floatp x, floatp y, gs_fixed_point *ppt)
277{ ppt->x = float2fixed(x * pmat->xx) + pmat->tx_fixed;
278 ppt->y = float2fixed(y * pmat->yy) + pmat->ty_fixed;
279 if ( !is_fzero(pmat->yx) )
280 ppt->x += float2fixed(y * pmat->yx);
281 if ( !is_fzero(pmat->xy) )
282 ppt->y += float2fixed(x * pmat->xy);
283 return 0;
284}
285
286/* Transform a distance with a fixed-point result. */
287int
288gs_distance_transform2fixed(register gs_matrix_fixed *pmat,
289 floatp dx, floatp dy, gs_fixed_point *ppt)
290{ ppt->x = float2fixed(dx * pmat->xx);
291 ppt->y = float2fixed(dy * pmat->yy);
292 if ( !is_fzero(pmat->yx) )
293 ppt->x += float2fixed(dy * pmat->yx);
294 if ( !is_fzero(pmat->xy) )
295 ppt->y += float2fixed(dx * pmat->xy);
296 return 0;
297}