1
0
mirror of git://projects.qi-hardware.com/ben-scans.git synced 2024-11-21 21:45:55 +02:00

Instead of performing the tranformations stepwise for each point, pre-calculate

them once.

- solidify/matrix.h, solidify/matrix.c: 2D matrix operations
- solidify/Makefile (OBJS): added matrix.o
- solidify/face.h (struct matrix): moved to solidify/matrix.h
- solidify/overlap.c (point, merge_matrix, draw_map): precalculate a single
  vA+b transformation instead of stepwise transforming the vector inside
  the loop
- solidify/overlap.c (do_shift): now that the math is right (or at least
  better), reverse the shift
This commit is contained in:
Werner Almesberger 2010-09-24 16:42:36 -03:00
parent 37e840fa77
commit 898970b3dd
5 changed files with 219 additions and 30 deletions

View File

@ -12,8 +12,8 @@
SHELL = /bin/bash
OBJS = array.o face.o histo.o level.o overlap.o solid.o solidify.o style.o \
util.o
OBJS = array.o face.o histo.o level.o matrix.o overlap.o solid.o solidify.o \
style.o util.o
CFLAGS_WARN = -Wall -Wshadow -Wmissing-prototypes \
-Wmissing-declarations -Wno-format-zero-length

View File

@ -14,21 +14,9 @@
#define FACE_H
#include "array.h"
#include "matrix.h"
/*
* 2D transformation:
*
* x' = x*a[0][0]+y*a[0][1]+b[0]
* y' = x*a[1][0]+y*a[1][1]+b[1]
*/
struct matrix {
double a[2][2];
double b[2];
};
struct face {
struct array *a;
int sx, sy; /* size */
@ -47,4 +35,4 @@ static inline double face_z0(const struct face *f, int x, int y)
struct face *read_face(const char *name);
#endif /* FACE_H */
#endif /* !FACE_H */

66
solidify/matrix.c Normal file
View File

@ -0,0 +1,66 @@
/*
* matrix.c - 2D matrix operations
*
* Written 2010 by Werner Almesberger
* Copyright 2010 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <assert.h>
#include "matrix.h"
void matrix_identity(struct matrix *m)
{
m->a[0][0] = m->a[1][1] = 1;
m->a[0][1] = m->a[1][0] = 0;
m->b[0] = m->b[1] = 0;
}
void matrix_invert(const double m[2][2], double res[2][2])
{
double det = m[0][0]*m[1][1]-m[0][1]*m[1][0];
assert(res != (void *) m);
res[0][0] = m[1][1]/det;
res[0][1] = -m[0][1]/det;
res[1][0] = -m[1][0]/det;
res[1][1] = m[0][0]/det;
}
void matrix_mult(double a[2][2], double b[2][2], double res[2][2])
{
assert(res != a);
assert(res != b);
res[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0];
res[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1];
res[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0];
res[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1];
}
void matrix_multv(const double v[2], double m[2][2], double res[2])
{
double tmp;
tmp = v[0]*m[0][0]+v[1]*m[0][1];
res[1] = v[0]*m[1][0]+v[1]*m[1][1];
res[0] = tmp;
}
void matrix_copy(double from[2][2], double to[2][2])
{
to[0][0] = from[0][0];
to[0][1] = from[0][1];
to[1][0] = from[1][0];
to[1][1] = from[1][1];
}

45
solidify/matrix.h Normal file
View File

@ -0,0 +1,45 @@
/*
* matrix.h - 2D matrix operations
*
* Written 2010 by Werner Almesberger
* Copyright 2010 by Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef MATRIX_H
#define MATRIX_H
/*
* 2D transformation:
*
* x' = x*a[0][0]+y*a[0][1]+b[0]
* y' = x*a[1][0]+y*a[1][1]+b[1]
*/
struct matrix {
double a[2][2];
double b[2];
};
void matrix_identity(struct matrix *m);
void matrix_invert(const double m[2][2], double res[2][2]);
void matrix_mult(double a[2][2], double b[2][2], double res[2][2]);
void matrix_multv(const double v[2], double m[2][2], double res[2]);
void matrix_copy(double from[2][2], double to[2][2]);
static inline void matrix_map(int x, int y, const struct matrix *m,
double *res_x, double *res_y)
{
*res_x = x*m->a[0][0]+y*m->a[0][1]+m->b[0];
*res_y = x*m->a[1][0]+y*m->a[1][1]+m->b[1];
}
#endif /* !MATRIX_H */

View File

@ -92,22 +92,16 @@ static double zmix(struct face *f, double x, double y)
* - our model runs from min_x to max_x. Its center is at cx.
*/
static void point(const struct solid *s, int x, int y, guchar *p)
static void point(const struct solid *s, int x, int y, guchar *p,
const struct matrix *ma, const struct matrix *mb)
{
double za, zb, z;
int xa, xb, ya, yb;
double xaf, xbf, yaf, ybf;
xa = x-sx(s)/2;
ya = y-sy(s)/2;
xaf = xa*s->a->m.a[0][0]+ya*s->a->m.a[0][1]+s->a->m.b[0]+s->a->cx;
yaf = xa*s->a->m.a[1][0]+ya*s->a->m.a[1][1]+s->a->m.b[1]+s->a->cy;
za = zmix(s->a, xaf, yaf);
matrix_map(x, y, ma, &xaf, &yaf);
matrix_map(x, y, mb, &xbf, &ybf);
xb = x-sx(s)/2;
yb = (sy(s)-1)/2-y;
xbf = xb*s->b->m.a[0][0]+yb*s->b->m.a[0][1]+s->b->m.b[0]+s->b->cx;
ybf = xb*s->b->m.a[1][0]+yb*s->b->m.a[1][1]+s->b->m.b[1]+s->b->cy;
za = zmix(s->a, xaf, yaf);
zb = zmix(s->b, xbf, ybf);
if (za == UNDEF_F && zb == UNDEF_F)
@ -158,19 +152,115 @@ static void point(const struct solid *s, int x, int y, guchar *p)
}
static void merge_matrix(struct matrix *m, const struct solid *s,
const struct face *f)
{
double tm[2][2], tm2[2][2];
double tv[2];
double f_x, f_y;
/*
* Finally, we convert to model matrix coordinates.
*
* v' = v+c
*/
m->b[0] += f->cx;
m->b[1] += f->cy;
/*
* Apply shrinkage caused by rotation out of z0. We use that
* cos a = sqrt(1-sin^2 a)
*/
f_x = 1.0/sqrt(1-f->fx*f->fx);
f_y = 1.0/sqrt(1-f->fy*f->fy);
m->a[0][0] *= f_x;
m->a[0][1] *= f_x;
m->b[0] *= f_x;
m->a[1][0] *= f_y;
m->a[1][1] *= f_y;
m->b[1] *= f_y;
/*
* The transformation matrix f->m describes a transformation of
* (centered) model coordinates. We therefore have to reverse it:
*
* v = v'A+b
* v-b = v'A
* (v-b)A^-1 = v'
* vA^-1-bA^-1 = v'
*/
matrix_invert(f->m.a, tm);
matrix_multv(f->m.b, tm, tv);
tv[0] = -tv[0];
tv[1] = -tv[1];
/*
* Merge with the transformations we have so far:
*
* v' = vA1+b1 the transformation we have so far
* v'' = v'A2+b2 the transformation we apply
*
* v'' = (vA1+b1)A2+b2
* v'' = vA1A2+b1A2+b2
*/
/*
* So far, the theory. To make it really work, we have to calculate
* v'' = vA1A2+b1+b2
* duh ?!?
*/
matrix_mult(m->a, tm, tm2); /* A1A2 */
matrix_copy(tm2, m->a);
// matrix_multv(m->b, tm, m->b); /* b1A2 */
m->b[0] += tv[0]; /* b2 */
m->b[1] += tv[1];
/*
* Our input is a screen coordinate, its origin is in a corner so we
* first have to make it center-based:
*
* v' = (v-s/2)A+b
* v' = vA+(b-s/2*A)
*/
tv[0] = sx(s)/2;
tv[1] = sy(s)/2;
matrix_multv(tv, m->a, tv);
m->b[0] -= tv[0];
m->b[1] -= tv[1];
}
static void draw_map(GtkWidget *widget, struct solid *s)
{
guchar *rgbbuf, *p;
int x, y;
struct matrix ma = {
.a = { { 1, 0 }, { 0, 1 } },
.b = { 0, 0 },
};
struct matrix mb = {
.a = { { 1, 0 }, { 0, 1 } }, /* @@@ why not a[1][1] = -1 ? */
.b = { 0, 0 },
};
rgbbuf = p = calloc(sx(s)*sy(s), 3);
if (!rgbbuf) {
perror("calloc");
exit(1);
}
merge_matrix(&ma, s, s->a);
merge_matrix(&mb, s, s->b);
for (y = sy(s)-1; y >= 0; y--)
for (x = 0; x != sx(s) ; x++) {
point(s, x, y, p);
point(s, x, y, p, &ma, &mb);
p += 3;
}
gdk_draw_rgb_image(widget->window,
@ -221,8 +311,8 @@ static void rotate(struct matrix *m, double r)
static void do_shift(struct matrix *m, int dx, int dy)
{
m->b[0] -= dx;
m->b[1] += dy;
m->b[0] += dx;
m->b[1] -= dy;
}