#ifndef lint static char sccsid[] = "@(#)transform.c 9.3 88/01/19 Copyright 1985 Sun Micro"; #endif /* * Copyright (c) 1985 by Sun Microsystems, Inc. */ /*- Routines to deal with transformation matrices transform.c, Thu Sep 19 12:27:04 1985 James Gosling, Sun Microsystems */ #ifdef REF #include #include #endif #include "shape.h" #include "canvas.h" #ifdef PREVIEW #include #include <../server/PostScript.h> struct execution_environment *curee; int preview_show; int watchforfont; int preview_y1; int preview_y2; #endif static transform_revision; cs_currentmatrix(gc, matrix) register struct graphics_context *gc; register fract matrix[3][2]; { /* * The only reason that currentmatrix isnt just a trivial copy is that we * have to translate from pixrect coordinates to PostScript device * coordinates. Pixrect coordinates have (0,0) in the upper left, with y * increasing down. PostScript has (0,0) in the lower left with y * increasing up. Technically, this compensation doesnt need to be done, * but there seem to be hidden assumptions that a transform matrix of [1 0 * 0 1 0 0] gives device coordinates in the normal PostScript orientation */ bcopy(gc->transform.matrix, matrix, sizeof gc->transform.matrix); matrix[0][1] = -matrix[0][1]; matrix[1][1] = -matrix[1][1]; if (gc->canvas) matrix[2][1] = fracti(gc->canvas->pr_height /* - 1 */ ) - matrix[2][1]; } cs_setmatrix(gc, matrix) register struct graphics_context *gc; register fract matrix[3][2]; { if (matrix) { gc->transform.matrix[0][0] = matrix[0][0]; gc->transform.matrix[0][1] = -matrix[0][1]; gc->transform.matrix[1][0] = matrix[1][0]; gc->transform.matrix[1][1] = -matrix[1][1]; gc->transform.matrix[2][0] = matrix[2][0]; gc->transform.matrix[2][1] = matrix[2][1]; if (gc->canvas) gc->transform.matrix[2][1] = fracti(gc->canvas->pr_height /* - 1 */ ) - gc->transform.matrix[2][1]; } if (gc->transform.matrix[0][1] == 0 && gc->transform.matrix[1][0] == 0) if (gc->transform.matrix[0][0] == fracti(1)) if (gc->transform.matrix[1][1] == fracti(1)) { gc->transform.kind = tro_unit; gc->transform.revision = 1; } else if (gc->transform.matrix[1][1] == fracti(-1)) { gc->transform.kind = tro_neg; gc->transform.revision = 3; } else { gc->transform.kind = tro_scale; gc->transform.revision = ++transform_revision << 1; } else { register Max_coeff, t; if ((Max_coeff = gc->transform.matrix[0][0]) < 0) Max_coeff = -Max_coeff; if ((t = gc->transform.matrix[1][1]) < 0) t = -t; if (t > Max_coeff) Max_coeff = t; if (Max_coeff <= fracti(1)) gc->transform.kind = tro_scale; else { gc->transform.kind = tro_scalecheck; gc->transform.checklimit = frdiv(FRHUGE / 2, Max_coeff); } gc->transform.revision = ++transform_revision << 1; } else { register Max_coeff, t; if ((Max_coeff = gc->transform.matrix[0][0]) < 0) Max_coeff = -Max_coeff; if ((t = gc->transform.matrix[0][1]) < 0) t = -t; if (t > Max_coeff) Max_coeff = t; if ((t = gc->transform.matrix[1][1]) < 0) t = -t; if (t > Max_coeff) Max_coeff = t; if ((t = gc->transform.matrix[1][0]) < 0) t = -t; if (t > Max_coeff) Max_coeff = t; if (Max_coeff <= fracti(1)) gc->transform.kind = tro_none; else { gc->transform.kind = tro_check; gc->transform.checklimit = frdiv(FRHUGE / 2, Max_coeff); } gc->transform.revision = ++transform_revision << 1; } gc->transform.revision <<= 1; if (gc->printermatch) gc->transform.revision |= 1; gc->transform.rigid = 0; gc->transform.simplescale = 0; if (gc->transform.matrix[0][0] == -gc->transform.matrix[1][1]) { if (gc->transform.matrix[1][0] == gc->transform.matrix[0][1]) { gc->transform.rigid = 1; if (gc->transform.matrix[1][0] == 0) gc->transform.simplescale = 1; } else if (gc->transform.matrix[1][0] == -gc->transform.matrix[0][1]) gc->transform.rigid = 1; } else if (gc->transform.matrix[0][0] == gc->transform.matrix[1][1] && (gc->transform.matrix[1][0] == gc->transform.matrix[0][1] || gc->transform.matrix[1][0] == -gc->transform.matrix[0][1])) gc->transform.rigid = 1; gc->transform.floatexists = 0; if (gc->strokequality && gc->linewidth) cv_evaluatestrokequality(gc); } fl_matrix_concat(m0, m1, mnew) register FLMATRIX m0, m1, mnew; { mnew[0][0] = m0[0][0] * m1[0][0] + m0[0][1] * m1[1][0]; mnew[1][0] = m0[1][0] * m1[0][0] + m0[1][1] * m1[1][0]; mnew[2][0] = m0[2][0] * m1[0][0] + m0[2][1] * m1[1][0] + m1[2][0]; mnew[0][1] = m0[0][0] * m1[0][1] + m0[0][1] * m1[1][1]; mnew[1][1] = m0[1][0] * m1[0][1] + m0[1][1] * m1[1][1]; mnew[2][1] = m0[2][0] * m1[0][1] + m0[2][1] * m1[1][1] + m1[2][1]; } /* end fl_matrix_concat */ FMATRIX * sh_matrix_concat(m1, m2) register fract m1[]; register fract m2[]; { static FMATRIX result; result[0][0] = frmul(m1[0], m2[0]) + frmul(m1[1], m2[2]); result[0][1] = frmul(m1[0], m2[1]) + frmul(m1[1], m2[3]); result[1][0] = frmul(m1[2], m2[0]) + frmul(m1[3], m2[2]); result[1][1] = frmul(m1[2], m2[1]) + frmul(m1[3], m2[3]); result[2][0] = frmul(m1[4], m2[0]) + frmul(m1[5], m2[2]) + m2[4]; result[2][1] = frmul(m1[4], m2[1]) + frmul(m1[5], m2[3]) + m2[5]; return (FMATRIX *) result; } sh_matrix_mult(gc, ax, ay, ak, bx, by, bk) register struct graphics_context *gc; fract *ax, *ay, *ak, *bx, *by, *bk; { fract matrix[3][2]; matrix[0][0] = 0; matrix[1][0] = 0; matrix[2][0] = gc->transform.matrix[2][0]; matrix[0][1] = 0; matrix[1][1] = 0; if (gc->canvas) matrix[2][1] = fracti(gc->canvas->pr_height /* - 1 */ ) - gc->transform.matrix[2][1]; else matrix[2][1] = gc->transform.matrix[2][1]; gc->transform.floatexists = 0; if (ax) { matrix[0][0] += frmul(gc->transform.matrix[0][0], *ax); matrix[0][1] += frmul(-gc->transform.matrix[0][1], *ax); } else { matrix[0][0] += gc->transform.matrix[0][0]; matrix[0][1] += -gc->transform.matrix[0][1]; } if (ay) { matrix[1][0] += frmul(gc->transform.matrix[0][0], *ay); matrix[1][1] += frmul(-gc->transform.matrix[0][1], *ay); } if (ak) { matrix[2][0] += frmul(gc->transform.matrix[0][0], *ak); matrix[2][1] += frmul(-gc->transform.matrix[0][1], *ak); } if (bx) { matrix[0][0] += frmul(gc->transform.matrix[1][0], *bx); matrix[0][1] += frmul(-gc->transform.matrix[1][1], *bx); } if (by) { matrix[1][0] += frmul(gc->transform.matrix[1][0], *by); matrix[1][1] += frmul(-gc->transform.matrix[1][1], *by); } else { matrix[1][0] += gc->transform.matrix[1][0]; matrix[1][1] += -gc->transform.matrix[1][1]; } if (bk) { matrix[2][0] += frmul(gc->transform.matrix[1][0], *bk); matrix[2][1] += frmul(-gc->transform.matrix[1][1], *bk); } cs_setmatrix(gc, matrix); } cs_initmatrix(gc) register struct graphics_context *gc; { register struct canvas *cv = (struct canvas *) gc->canvas; if (gc->canvas && cv_is_canvas(gc->canvas)) { if (cv->overlay) cv = cv->parent; cs_setmatrix(gc, cv->matrix); } else { static fract matrix[3][2] = {fracti(1), 0, 0, fracti(1), 0, 0}; cs_setmatrix(gc, matrix); } } /* Inverse transform (x,y) to (shape_point.x, shape_point.y) via transform t */ sh_fritransform(t, x, y) register struct transform *t; register fract x, y; { switch (t->kind) { case tro_unit: shape_point.x = x - t->matrix[2][0]; shape_point.y = y - t->matrix[2][1]; break; case tro_neg: shape_point.x = x - t->matrix[2][0]; shape_point.y = t->matrix[2][1] - y; break; case tro_scale: case tro_scalecheck: shape_point.x = frdiv(x - t->matrix[2][0], t->matrix[0][0]); shape_point.y = frdiv(y - t->matrix[2][1], t->matrix[1][1]); break; default:{ register fract num, denom; extern short fract_overflows; fract_overflows = 0; denom = vfrmul(t->matrix[1][0], t->matrix[0][1]) - vfrmul(t->matrix[1][1], t->matrix[0][0]); num = vfrmul(t->matrix[1][0], y - t->matrix[2][1]) - vfrmul(t->matrix[1][1], x - t->matrix[2][0]); if (fract_overflows) { /* There must be a more elegant way of * coping with overflow here */ sh_flitransform(t, floatfr(x), floatfr(y)); shape_point.x = fractf(shape_rpoint.x); shape_point.y = fractf(shape_rpoint.y); break; } if (denom == 0) { shape_point.x = 0; shape_point.y = 0; } else { shape_point.x = frdiv(num, denom); if (abs(t->matrix[1][1]) > abs(t->matrix[1][0])) shape_point.y = frdiv(y - frmul(t->matrix[0][1], shape_point.x) - t->matrix[2][1], t->matrix[1][1]); else if (t->matrix[1][0]) shape_point.y = frdiv(x - frmul(t->matrix[0][0], shape_point.x) - t->matrix[2][0], t->matrix[1][0]); else shape_point.y = 0; } } } } sh_flitransform(t, x, y) register struct transform *t; register float x, y; { if (!t->floatexists) sh_MakeFloatVersion(t); switch (t->kind) { case tro_unit: shape_rpoint.x = x - t->rmatrix[2][0]; shape_rpoint.y = y - t->rmatrix[2][1]; break; case tro_neg: shape_rpoint.x = x - t->rmatrix[2][0]; shape_rpoint.y = t->rmatrix[2][1] - y; break; default: { register float num, denom; denom = t->rmatrix[1][0] * t->rmatrix[0][1] - t->rmatrix[1][1] * t->rmatrix[0][0]; num = t->rmatrix[1][0] * (y - t->rmatrix[2][1]) - t->rmatrix[1][1] * (x - t->rmatrix[2][0]); if (denom == 0) { shape_rpoint.x = 0; shape_rpoint.y = 0; } else { shape_rpoint.x = num / denom; if (t->rmatrix[1][1]) shape_rpoint.y = (y - t->rmatrix[0][1] * shape_rpoint.x - t->rmatrix[2][1]) / t->rmatrix[1][1]; else if (t->rmatrix[1][0]) shape_rpoint.y = (x - t->rmatrix[0][0] * shape_rpoint.x - t->rmatrix[2][0]) / t->rmatrix[1][0]; else shape_rpoint.y = 0; } } } } /* * Inverse delta transform (x,y) to (shape_point.x, shape_point.y) via * transform t */ sh_fridtransform(t, x, y) register struct transform *t; register fract x, y; { switch (t->kind) { case tro_unit: shape_point.x = x; shape_point.y = y; break; case tro_neg: shape_point.x = x; shape_point.y = -y; break; case tro_scale: case tro_scalecheck: shape_point.x = frdiv(x, t->matrix[0][0]); shape_point.y = frdiv(y, t->matrix[1][1]); break; default: { register fract num, denom; denom = frmul(t->matrix[1][0], t->matrix[0][1]) - frmul(t->matrix[1][1], t->matrix[0][0]); num = frmul(t->matrix[1][0], y) - frmul(t->matrix[1][1], x); if (denom == 0) { shape_point.x = 0; shape_point.y = 0; } else { shape_point.x = frdiv(num, denom); if (t->matrix[1][1]) shape_point.y = frdiv(y - frmul(t->matrix[0][1], shape_point.x), t->matrix[1][1]); else if (t->matrix[1][0]) shape_point.y = frdiv(x - frmul(t->matrix[0][0], shape_point.x), t->matrix[1][0]); else shape_point.y = 0; } } } } sh_flidtransform(t, x, y) register struct transform *t; register float x, y; { if (!t->floatexists) sh_MakeFloatVersion(t); switch (t->kind) { case tro_unit: shape_rpoint.x = x; shape_rpoint.y = y; break; case tro_neg: shape_rpoint.x = x; shape_rpoint.y = -y; break; default: { register fract num, denom; denom = t->rmatrix[1][0] * t->rmatrix[0][1] - t->rmatrix[1][1] * t->rmatrix[0][0]; num = t->rmatrix[1][0] * y - t->rmatrix[1][1] * x; if (denom == 0) { shape_rpoint.x = 0; shape_rpoint.y = 0; } else { shape_rpoint.x = num / denom; if (t->rmatrix[1][1]) shape_rpoint.y = (y - t->rmatrix[0][1] * shape_rpoint.x) / t->rmatrix[1][1]; else if (t->rmatrix[1][0]) shape_rpoint.y = (x - t->rmatrix[0][0] * shape_rpoint.x) / t->rmatrix[1][0]; else shape_rpoint.y = 0; } } } } /* Transform (x,y) to pos via transform t */ sh_frtransform(t, x, y) register struct transform *t; register fract x, y; { int ht; extern short fract_overflows; #ifdef PREVIEW static int oldshapey; #endif switch (t->kind) { case tro_unit: shape_point.x = x + t->matrix[2][0]; shape_point.y = y + t->matrix[2][1]; break; case tro_neg: shape_point.x = x + t->matrix[2][0]; shape_point.y = t->matrix[2][1] - y; break; case tro_scalecheck: fract_overflows = 0; shape_point.x = vfrmul(x, t->matrix[0][0]) + t->matrix[2][0]; shape_point.y = vfrmul(y, t->matrix[1][1]) + t->matrix[2][1]; if (fract_overflows) { shape_point.x = FRHUGE; shape_point.y = FRHUGE; } break; case tro_scale: shape_point.x = frmul(x, t->matrix[0][0]) + t->matrix[2][0]; shape_point.y = frmul(y, t->matrix[1][1]) + t->matrix[2][1]; break; case tro_check: if (x > t->checklimit || x < -t->checklimit || y > t->checklimit || y < -t->checklimit) { fract_overflows = 0; shape_point.x = vfradd(vfradd(vfrmul(x, t->matrix[0][0]), vfrmul(y, t->matrix[1][0])), t->matrix[2][0]); shape_point.y = vfradd(vfradd(vfrmul(x, t->matrix[0][1]), vfrmul(y, t->matrix[1][1])), t->matrix[2][1]); if (fract_overflows) { shape_point.x = FRHUGE; shape_point.y = FRHUGE; } break; } default: shape_point.x = frmul(x, t->matrix[0][0]) + frmul(y, t->matrix[1][0]) + t->matrix[2][0]; shape_point.y = frmul(x, t->matrix[0][1]) + frmul(y, t->matrix[1][1]) + t->matrix[2][1]; } #ifdef PREVIEW if (oldshapey != shape_point.y && y != 0 && preview_show) { preview_show = 0; watchforfont = 1; updatescreen(floorfr(oldshapey) + preview_y1, floorfr(oldshapey) + preview_y2); getfontht(&curee->gontext, &preview_y1, &preview_y2); oldshapey = shape_point.y; } #endif } sh_MakeFloatVersion(t) register struct transform *t; { if (!t->floatexists) { t->floatexists = 1; t->rmatrix[0][0] = floatfr(t->matrix[0][0]); t->rmatrix[1][0] = floatfr(t->matrix[1][0]); t->rmatrix[2][0] = floatfr(t->matrix[2][0]); t->rmatrix[0][1] = floatfr(t->matrix[0][1]); t->rmatrix[1][1] = floatfr(t->matrix[1][1]); t->rmatrix[2][1] = floatfr(t->matrix[2][1]); } } /* Transform (x,y) to pos via transform t */ sh_fltransform(t, x, y) register struct transform *t; register float x, y; { if (!t->floatexists) sh_MakeFloatVersion(t); switch (t->kind) { case tro_unit: shape_point.x = fractf(x + t->rmatrix[2][0]); shape_point.y = fractf(y + t->rmatrix[2][1]); break; case tro_neg: shape_point.x = fractf(x + t->rmatrix[2][0]); shape_point.y = fractf(t->rmatrix[2][1] - y); break; default: shape_point.x = fractf(x * t->rmatrix[0][0] + y * t->rmatrix[1][0] + t->rmatrix[2][0]); shape_point.y = fractf(x * t->rmatrix[0][1] + y * t->rmatrix[1][1] + t->rmatrix[2][1]); } } /* Delta transforms */ /* Delta transform (x,y) to pos via matrix */ sh_frmdtransform(matrix, x, y) register fract matrix[3][2]; register fract x, y; { shape_point.x = frmul(x, matrix[0][0]) + frmul(y, matrix[1][0]); shape_point.y = frmul(x, matrix[0][1]) + frmul(y, matrix[1][1]); } /* Delta transform (x,y) to pos via transform t */ sh_frdtransform(t, x, y) register struct transform *t; register fract x, y; { switch (t->kind) { case tro_unit: shape_point.x = x; shape_point.y = y; break; case tro_neg: shape_point.x = x; shape_point.y = -y; break; case tro_scale: shape_point.x = frmul(x, t->matrix[0][0]); shape_point.y = frmul(y, t->matrix[1][1]); break; default: shape_point.x = frmul(x, t->matrix[0][0]) + frmul(y, t->matrix[1][0]); shape_point.y = frmul(x, t->matrix[0][1]) + frmul(y, t->matrix[1][1]); } } /* Delta transform (x,y) to pos via transform t */ sh_fldtransform(t, x, y) register struct transform *t; register float x, y; { if (!t->floatexists) sh_MakeFloatVersion(t); switch (t->kind) { case tro_unit: shape_point.x = fractf(x); shape_point.y = fractf(y); break; case tro_neg: shape_point.x = fractf(x); shape_point.y = -fractf(y); break; default: shape_point.x = fractf(x * t->rmatrix[0][0] + y * t->rmatrix[1][0]); shape_point.y = fractf(x * t->rmatrix[0][1] + y * t->rmatrix[1][1]); } } cs_frrotate(gc, angle) register struct graphics_context *gc; { fract xf, yf, mxf; frsincosd(angle, &xf, &yf); mxf = -xf; sh_matrix_mult(gc, &yf, &mxf, 0, &xf, &yf, 0); } cs_frtranslate(gc, dx, dy) struct graphics_context *gc; fract dx, dy; { gc->transform.floatexists = 0; switch (gc->transform.kind) { case tro_neg: gc->transform.matrix[2][0] += dx; gc->transform.matrix[2][1] -= dy; break; case tro_unit: gc->transform.matrix[2][0] += dx; gc->transform.matrix[2][1] += dy; break; default: gc->transform.matrix[2][0] += frmul(gc->transform.matrix[0][0], dx); gc->transform.matrix[2][1] += frmul(gc->transform.matrix[0][1], dx); gc->transform.matrix[2][0] += frmul(gc->transform.matrix[1][0], dy); gc->transform.matrix[2][1] += frmul(gc->transform.matrix[1][1], dy); break; } } cs_frscale(gc, sx, sy) register struct graphics_context *gc; { switch (gc->transform.kind) { case tro_neg: sy = -sy; case tro_unit: gc->transform.matrix[0][0] = sx; gc->transform.matrix[1][1] = sy; cs_setmatrix(gc, 0); break; default: sh_matrix_mult(gc, &sx, 0, 0, 0, &sy, 0); } } FMATRIX * cs_defaultmatrix(canvas) register struct canvas *canvas; { static FMATRIX result; if (cv_is_canvas(canvas)) { result[0][0] = canvas->scene->matrix[0][0]; result[0][1] = canvas->scene->matrix[0][1]; result[1][0] = canvas->scene->matrix[1][0]; result[1][1] = canvas->scene->matrix[1][1]; } else { result[0][0] = 1; result[0][1] = 0; result[1][0] = 0; result[1][1] = 1; } result[2][0] = 0; result[2][1] = 0; return (FMATRIX *) result; } cv_evaluatestrokequality(gc) register struct graphics_context *gc; { if (gc->linewidth <= 0) { if (gc->strokequality > 0) gc->strokequality = -gc->strokequality; } else { sh_frdtransform(&gc->transform, gc->linewidth, 0); if (abs(shape_point.x) + abs(shape_point.y) > fraction(3, 2)) { if (gc->strokequality < 0) gc->strokequality = -gc->strokequality; } else if (gc->strokequality > 0) gc->strokequality = -gc->strokequality; } } /* Given a bounding box in device space, return a bounding box in * user space that bounds it */ cs_bbitransform(gc, dbb, ubb) register struct graphics_context *gc; register struct fcorner_bbox *dbb, *ubb; { sh_fritransform(&gc->transform, dbb->ll.x, dbb->ll.y); ubb->ll = shape_point; sh_fritransform(&gc->transform, dbb->ur.x, dbb->ur.y); if (ubb->ll.x < shape_point.x) ubb->ur.x = shape_point.x; else ubb->ur.x = ubb->ll.x, ubb->ll.x = shape_point.x; if (ubb->ll.y < shape_point.y) ubb->ur.y = shape_point.y; else ubb->ur.y = ubb->ll.y, ubb->ll.y = shape_point.y; if (gc->transform.matrix[0][1] != 0 || gc->transform.matrix[1][0] != 0) { sh_fritransform(&gc->transform, dbb->ll.x, dbb->ur.y); if (shape_point.x < ubb->ll.x) ubb->ll.x = shape_point.x; if (shape_point.x > ubb->ur.x) ubb->ur.x = shape_point.x; if (shape_point.y < ubb->ll.y) ubb->ll.y = shape_point.y; if (shape_point.y > ubb->ur.y) ubb->ur.y = shape_point.y; sh_fritransform(&gc->transform, dbb->ur.x, dbb->ll.y); if (shape_point.x < ubb->ll.x) ubb->ll.x = shape_point.x; if (shape_point.x > ubb->ur.x) ubb->ur.x = shape_point.x; if (shape_point.y < ubb->ll.y) ubb->ll.y = shape_point.y; if (shape_point.y > ubb->ur.y) ubb->ur.y = shape_point.y; } }