blob: 7867a721e18a83533fb1b0b18daf1417f04d1ad9 [file] [log] [blame]
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
- This software is distributed in the hope that it will be
- useful, but with NO WARRANTY OF ANY KIND.
- No author or distributor accepts responsibility to anyone for the
- consequences of using this software, or for whether it serves any
- particular purpose or works at all, unless he or she says so in
- writing. Everyone is granted permission to copy, modify and
- redistribute this source code, for commercial or non-commercial
- purposes, with the following restrictions: (1) the origin of this
- source code must not be misrepresented; (2) modified versions must
- be plainly marked as such; and (3) this notice may not be removed
- or altered from any source or modified source distribution.
*====================================================================*/
/*
* affinecompose.c
*
* Composable coordinate transforms
* l_float32 *createMatrix2dTranslate()
* l_float32 *createMatrixScale()
* l_float32 *createMatrixRotate()
*
* Special coordinate transforms on pta
* PTA *ptaTranslate()
* PTA *ptaScale()
* PTA *ptaRotate()
*
* Special coordinate transforms on boxa
* BOXA *boxaTranslate()
* BOXA *boxaScale()
* BOXA *boxaRotate()
*
* General coordinate transform on pta and boxa
* PTA *ptaAffineTransform()
* BOXA *boxaAffineTransform()
*
* Matrix operations
* l_int32 l_productMatVec()
* l_int32 l_productMat2()
* l_int32 l_productMat3()
* l_int32 l_productMat4()
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "allheaders.h"
/*-------------------------------------------------------------*
* Composable coordinate transforms *
*-------------------------------------------------------------*/
/*!
* createMatrix2dTranslate()
*
* Input: transx (x component of translation wrt. the origin)
* transy (y component of translation wrt. the origin)
* Return: 3x3 transform matrix, or null on error
*
* Notes;
* (1) The translation is equivalent to:
* v' = Av
* where v and v' are 1x3 column vectors in the form
* v = [x, y, 1]^ (^ denotes transpose)
* and the affine tranlation matrix is
* A = [ 1 0 tx
* 0 1 ty
* 0 0 1 ]
*
* (2) We consider translation as with respect to a fixed origin.
* In a clipping operation, the origin moves and the points
* are fixed, and you use (-tx, -ty) where (tx, ty) is the
* translation vector of the origin.
*/
l_float32 *
createMatrix2dTranslate(l_float32 transx,
l_float32 transy)
{
l_float32 *mat;
PROCNAME("createMatrix2dTranslate");
if ((mat = (l_float32 *)CALLOC(9, sizeof(l_float32))) == NULL)
return (l_float32 *)ERROR_PTR("mat not made", procName, NULL);
mat[0] = mat[4] = mat[8] = 1;
mat[2] = transx;
mat[5] = transy;
return mat;
}
/*!
* createMatrix2dScale()
*
* Input: scalex (horizontal scale factor)
* scaley (vertical scale factor)
* Return: 3x3 transform matrix, or null on error
*
* Notes;
* (1) The scaling is equivalent to:
* v' = Av
* where v and v' are 1x3 column vectors in the form
* v = [x, y, 1]^ (^ denotes transpose)
* and the affine scaling matrix is
* A = [ sx 0 0
* 0 sy 0
* 0 0 1 ]
*
* (2) We consider scaling as with respect to a fixed origin.
* In other words, the origin is the only point that doesn't
* move in the scaling transform.
*/
l_float32 *
createMatrix2dScale(l_float32 scalex,
l_float32 scaley)
{
l_float32 *mat;
PROCNAME("createMatrix2dScale");
if ((mat = (l_float32 *)CALLOC(9, sizeof(l_float32))) == NULL)
return (l_float32 *)ERROR_PTR("mat not made", procName, NULL);
mat[0] = scalex;
mat[4] = scaley;
mat[8] = 1;
return mat;
}
/*!
* createMatrix2dRotate()
*
* Input: xc, yc (location of center of rotation)
* angle (rotation in radians; clockwise is positive)
* Return: 3x3 transform matrix, or null on error
*
* Notes;
* (1) The rotation is equivalent to:
* v' = Av
* where v and v' are 1x3 column vectors in the form
* v = [x, y, 1]^ (^ denotes transpose)
* and the affine rotation matrix is
* A = [ cosa -sina xc*(1-cosa) + yc*sina
* sina cosa yc*(1-cosa) - xc*sina
* 0 0 1 ]
*
* If the rotation is about the origin, (xc, yc) = (0, 0) and
* this simplifies to
* A = [ cosa -sina 0
* sina cosa 0
* 0 0 1 ]
*
* These relations follow from the following equations, which
* you can convince yourself are correct as follows. Draw a
* circle centered on (xc,yc) and passing through (x,y), with
* (x',y') on the arc at an angle 'a' clockwise from (x,y).
* [ Hint: cos(a + b) = cosa * cosb - sina * sinb
* sin(a + b) = sina * cosb + cosa * sinb ]
*
* x' - xc = (x - xc) * cosa - (y - yc) * sina
* y' - yc = (x - xc) * sina + (y - yc) * cosa
*/
l_float32 *
createMatrix2dRotate(l_float32 xc,
l_float32 yc,
l_float32 angle)
{
l_float32 sina, cosa;
l_float32 *mat;
PROCNAME("createMatrix2dRotate");
if ((mat = (l_float32 *)CALLOC(9, sizeof(l_float32))) == NULL)
return (l_float32 *)ERROR_PTR("mat not made", procName, NULL);
sina = sin(angle);
cosa = cos(angle);
mat[0] = mat[4] = cosa;
mat[1] = -sina;
mat[2] = xc * (1.0 - cosa) + yc * sina;
mat[3] = sina;
mat[5] = yc * (1.0 - cosa) - xc * sina;
mat[8] = 1;
return mat;
}
/*-------------------------------------------------------------*
* Special coordinate transforms on pta *
*-------------------------------------------------------------*/
/*!
* ptaTranslate()
*
* Input: ptas (for initial points)
* transx (x component of translation wrt. the origin)
* transy (y component of translation wrt. the origin)
* Return: ptad (translated points), or null on error
*
* Notes;
* (1) See createMatrix2dTranslate() for details of transform.
*/
PTA *
ptaTranslate(PTA *ptas,
l_float32 transx,
l_float32 transy)
{
l_int32 i, npts;
l_float32 x, y;
PTA *ptad;
PROCNAME("ptaTranslate");
if (!ptas)
return (PTA *)ERROR_PTR("ptas not defined", procName, NULL);
npts = ptaGetCount(ptas);
if ((ptad = ptaCreate(npts)) == NULL)
return (PTA *)ERROR_PTR("ptad not made", procName, NULL);
for (i = 0; i < npts; i++) {
ptaGetPt(ptas, i, &x, &y);
ptaAddPt(ptad, x + transx, y + transy);
}
return ptad;
}
/*!
* ptaScale()
*
* Input: ptas (for initial points)
* scalex (horizontal scale factor)
* scaley (vertical scale factor)
* Return: 0 if OK; 1 on error
*
* Notes;
* (1) See createMatrix2dScale() for details of transform.
*/
PTA *
ptaScale(PTA *ptas,
l_float32 scalex,
l_float32 scaley)
{
l_int32 i, npts;
l_float32 x, y;
PTA *ptad;
PROCNAME("ptaScale");
if (!ptas)
return (PTA *)ERROR_PTR("ptas not defined", procName, NULL);
npts = ptaGetCount(ptas);
if ((ptad = ptaCreate(npts)) == NULL)
return (PTA *)ERROR_PTR("ptad not made", procName, NULL);
for (i = 0; i < npts; i++) {
ptaGetPt(ptas, i, &x, &y);
ptaAddPt(ptad, scalex * x, scaley * y);
}
return ptad;
}
/*!
* ptaRotate()
*
* Input: ptas (for initial points)
* (xc, yc) (location of center of rotation)
* angle (rotation in radians; clockwise is positive)
* (&ptad) (<return> new locations)
* Return: 0 if OK; 1 on error
*
* Notes;
* (1) See createMatrix2dScale() for details of transform.
*/
PTA *
ptaRotate(PTA *ptas,
l_float32 xc,
l_float32 yc,
l_float32 angle)
{
l_int32 i, npts;
l_float32 x, y, xp, yp, sina, cosa;
PTA *ptad;
PROCNAME("ptaRotate");
if (!ptas)
return (PTA *)ERROR_PTR("ptas not defined", procName, NULL);
npts = ptaGetCount(ptas);
if ((ptad = ptaCreate(npts)) == NULL)
return (PTA *)ERROR_PTR("ptad not made", procName, NULL);
sina = sin(angle);
cosa = cos(angle);
for (i = 0; i < npts; i++) {
ptaGetPt(ptas, i, &x, &y);
xp = xc + (x - xc) * cosa - (y - yc) * sina;
yp = yc + (x - xc) * sina + (y - yc) * cosa;
ptaAddPt(ptad, xp, yp);
}
return ptad;
}
/*-------------------------------------------------------------*
* Special coordinate transforms on boxa *
*-------------------------------------------------------------*/
/*!
* boxaTranslate()
*
* Input: boxas
* transx (x component of translation wrt. the origin)
* transy (y component of translation wrt. the origin)
* Return: boxad (translated boxas), or null on error
*
* Notes;
* (1) See createMatrix2dTranslate() for details of transform.
*/
BOXA *
boxaTranslate(BOXA *boxas,
l_float32 transx,
l_float32 transy)
{
PTA *ptas, *ptad;
BOXA *boxad;
PROCNAME("boxaTranslate");
if (!boxas)
return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
ptas = boxaConvertToPta(boxas, 4);
ptad = ptaTranslate(ptas, transx, transy);
boxad = ptaConvertToBoxa(ptad, 4);
ptaDestroy(&ptas);
ptaDestroy(&ptad);
return boxad;
}
/*!
* boxaScale()
*
* Input: boxas
* scalex (horizontal scale factor)
* scaley (vertical scale factor)
* Return: boxad (scaled boxas), or null on error
*
* Notes;
* (1) See createMatrix2dScale() for details of transform.
*/
BOXA *
boxaScale(BOXA *boxas,
l_float32 scalex,
l_float32 scaley)
{
PTA *ptas, *ptad;
BOXA *boxad;
PROCNAME("boxaScale");
if (!boxas)
return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
ptas = boxaConvertToPta(boxas, 4);
ptad = ptaScale(ptas, scalex, scaley);
boxad = ptaConvertToBoxa(ptad, 4);
ptaDestroy(&ptas);
ptaDestroy(&ptad);
return boxad;
}
/*!
* boxaRotate()
*
* Input: boxas
* (xc, yc) (location of center of rotation)
* angle (rotation in radians; clockwise is positive)
* Return: boxad (scaled boxas), or null on error
*
* Notes;
* (1) See createMatrix2dRotate() for details of transform.
*/
BOXA *
boxaRotate(BOXA *boxas,
l_float32 xc,
l_float32 yc,
l_float32 angle)
{
PTA *ptas, *ptad;
BOXA *boxad;
PROCNAME("boxaRotate");
if (!boxas)
return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
ptas = boxaConvertToPta(boxas, 4);
ptad = ptaRotate(ptas, xc, yc, angle);
boxad = ptaConvertToBoxa(ptad, 4);
ptaDestroy(&ptas);
ptaDestroy(&ptad);
return boxad;
}
/*-------------------------------------------------------------*
* General affine coordinate transform *
*-------------------------------------------------------------*/
/*!
* ptaAffineTransform()
*
* Input: ptas (for initial points)
* mat (3x3 transform matrix; canonical form)
* Return: ptad (transformed points), or null on error
*/
PTA *
ptaAffineTransform(PTA *ptas,
l_float32 *mat)
{
l_int32 i, npts;
l_float32 vecs[3], vecd[3];
PTA *ptad;
PROCNAME("ptaAffineTransform");
if (!ptas)
return (PTA *)ERROR_PTR("ptas not defined", procName, NULL);
if (!mat)
return (PTA *)ERROR_PTR("transform not defined", procName, NULL);
vecs[2] = 1;
npts = ptaGetCount(ptas);
if ((ptad = ptaCreate(npts)) == NULL)
return (PTA *)ERROR_PTR("ptad not made", procName, NULL);
for (i = 0; i < npts; i++) {
ptaGetPt(ptas, i, &vecs[0], &vecs[1]);
l_productMatVec(mat, vecs, vecd, 3);
ptaAddPt(ptad, vecd[0], vecd[1]);
}
return ptad;
}
/*!
* boxaAffineTransform()
*
* Input: boxas
* mat (3x3 transform matrix; canonical form)
* Return: boxad (transformed boxas), or null on error
*/
BOXA *
boxaAffineTransform(BOXA *boxas,
l_float32 *mat)
{
PTA *ptas, *ptad;
BOXA *boxad;
PROCNAME("boxaAffineTransform");
if (!boxas)
return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL);
if (!mat)
return (BOXA *)ERROR_PTR("transform not defined", procName, NULL);
ptas = boxaConvertToPta(boxas, 4);
ptad = ptaAffineTransform(ptas, mat);
boxad = ptaConvertToBoxa(ptad, 4);
ptaDestroy(&ptas);
ptaDestroy(&ptad);
return boxad;
}
/*-------------------------------------------------------------*
* Matrix operations *
*-------------------------------------------------------------*/
/*!
* l_productMatVec()
*
* Input: mat (square matrix, as a 1-dimensional @size^2 array)
* vecs (input column vector of length @size)
* vecd (result column vector)
* size (matrix is @size x @size; vectors are length @size)
* Return: 0 if OK, 1 on error
*/
l_int32
l_productMatVec(l_float32 *mat,
l_float32 *vecs,
l_float32 *vecd,
l_int32 size)
{
l_int32 i, j;
PROCNAME("l_productMatVec");
if (!mat)
return ERROR_INT("matrix not defined", procName, 1);
if (!vecs)
return ERROR_INT("input vector not defined", procName, 1);
if (!vecd)
return ERROR_INT("result vector not defined", procName, 1);
for (i = 0; i < size; i++) {
vecd[i] = 0;
for (j = 0; j < size; j++) {
vecd[i] += mat[size * i + j] * vecs[j];
}
}
return 0;
}
/*!
* l_productMat2()
*
* Input: mat1 (square matrix, as a 1-dimensional size^2 array)
* mat2 (square matrix, as a 1-dimensional size^2 array)
* matd (square matrix; product stored here)
* size (of matrices)
* Return: 0 if OK, 1 on error
*/
l_int32
l_productMat2(l_float32 *mat1,
l_float32 *mat2,
l_float32 *matd,
l_int32 size)
{
l_int32 i, j, k, index;
PROCNAME("l_productMat2");
if (!mat1)
return ERROR_INT("matrix 1 not defined", procName, 1);
if (!mat2)
return ERROR_INT("matrix 2 not defined", procName, 1);
if (!matd)
return ERROR_INT("result matrix not defined", procName, 1);
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
index = size * i + j;
matd[index] = 0;
for (k = 0; k < size; k++)
matd[index] += mat1[size * i + k] * mat2[size * k + j];
}
}
return 0;
}
/*!
* l_productMat3()
*
* Input: mat1 (square matrix, as a 1-dimensional size^2 array)
* mat2 (square matrix, as a 1-dimensional size^2 array)
* mat3 (square matrix, as a 1-dimensional size^2 array)
* matd (square matrix; product stored here)
* size (of matrices)
* Return: 0 if OK, 1 on error
*/
l_int32
l_productMat3(l_float32 *mat1,
l_float32 *mat2,
l_float32 *mat3,
l_float32 *matd,
l_int32 size)
{
l_float32 *matt;
PROCNAME("l_productMat3");
if (!mat1)
return ERROR_INT("matrix 1 not defined", procName, 1);
if (!mat2)
return ERROR_INT("matrix 2 not defined", procName, 1);
if (!mat3)
return ERROR_INT("matrix 3 not defined", procName, 1);
if (!matd)
return ERROR_INT("result matrix not defined", procName, 1);
if ((matt = (l_float32 *)CALLOC(size * size, sizeof(l_float32))) == NULL)
return ERROR_INT("matt not made", procName, 1);
l_productMat2(mat1, mat2, matt, size);
l_productMat2(matt, mat3, matd, size);
FREE(matt);
return 0;
}
/*!
* l_productMat4()
*
* Input: mat1 (square matrix, as a 1-dimensional size^2 array)
* mat2 (square matrix, as a 1-dimensional size^2 array)
* mat3 (square matrix, as a 1-dimensional size^2 array)
* mat4 (square matrix, as a 1-dimensional size^2 array)
* matd (square matrix; product stored here)
* size (of matrices)
* Return: 0 if OK, 1 on error
*/
l_int32
l_productMat4(l_float32 *mat1,
l_float32 *mat2,
l_float32 *mat3,
l_float32 *mat4,
l_float32 *matd,
l_int32 size)
{
l_float32 *matt;
PROCNAME("l_productMat4");
if (!mat1)
return ERROR_INT("matrix 1 not defined", procName, 1);
if (!mat2)
return ERROR_INT("matrix 2 not defined", procName, 1);
if (!mat3)
return ERROR_INT("matrix 3 not defined", procName, 1);
if (!matd)
return ERROR_INT("result matrix not defined", procName, 1);
if ((matt = (l_float32 *)CALLOC(size * size, sizeof(l_float32))) == NULL)
return ERROR_INT("matt not made", procName, 1);
l_productMat3(mat1, mat2, mat3, matt, size);
l_productMat2(matt, mat4, matd, size);
FREE(matt);
return 0;
}