home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
038.lha
/
BCubic.c
< prev
next >
Wrap
Text File
|
1987-05-16
|
14KB
|
613 lines
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# bcubic.c
# bcubic.uue
# This archive created: Wed May 13 10:11:10 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'bcubic.c'" '(13600 characters)'
if test -f 'bcubic.c'
then
echo shar: "will not over-write existing file 'bcubic.c'"
else
cat << \!Funky!Stuff! > 'bcubic.c'
/*
* BCUBIC.C BEZIER CUBIC (3D) CURVED SURFACES
*
* (C)Copyright 1987 by Matthew Dillon, All Rights Reserved.
* Permission to redistribute for non-profit only.
* Permission to embellish the source however you wish and redistribute.
*
* Compiled with Aztec. You MUST use the +L option (32 bit ints) or you
* are sunk. You might have to specify more expression space for cc:
* (-E1000 should do it). Addtionaly, the source expects all Amiga
* symbols to be precompiled, and MY.LIB to exist. A good programmer
* would not have a problem removing the MY.LIB dependancies.
*
* Usage:
* Prop. Gadget changes granularity. Use the SELECT button to move
* control points on the X and Y axis. Use the MENU button to move
* control points on the Z axis.
*
* SELECT: right-left is X axis movement
* up-down is Y axis movement
* MENU: up-down is Z axis movement
*
* The 3D plane is a linear projection with the Z axis straight up,
* the X axis going to the right, and the Y axis going diagonal
* (lower-left to upper-right).
*
* NOTE:
* This program has been incredibly optimized. Do not attempt to
* gleam the matrix theory from the source unless you are familar
* with Bezier Cubic equations.
*/
#include <typedefs.h>
#include <xmisc.h>
#define ONE 512
#define SSF 9
#define SSFD (SSF*2)
#define MINGRAN 10 /* must be at least 10 */
#define CSIZE (ONE/MINGRAN+2)
typedef unsigned long ulong;
typedef unsigned short uword;
typedef unsigned char ubyte;
typedef struct PropInfo XPI;
typedef struct Image IM;
extern IMESS *GetMsg();
extern char *malloc();
extern GADGET Gadgets[];
#define MYGADGETS (WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE)
NW Nw = {
64, 64, 400, 120,
0, 1,
NEWSIZE|MOUSEBUTTONS|MOUSEMOVE|CLOSEWINDOW|GADGETDOWN|GADGETUP,
MYGADGETS|REPORTMOUSE|ACTIVATE|NOCAREREFRESH|RMBTRAP,
0, 0, (UBYTE *)"Bezier Cubic (3D curved surfaces), By Matthew Dillon", NULL, NULL,
32, 64, -1, -1, WBENCHSCREEN
};
WIN *Win;
RP *Rp;
short Ux, Uy, Mx, My;
short SStep = 512/16;
short TStep = 512/16;
main(ac, av)
char *av[];
{
register IMESS *mess;
char notdone = 1;
short mx, my, basex, basey, mm, mrmb = 0;
short gg, gy;
short pt = -1;
XPI *po;
disablebreak();
exiterr(!init_cubic(), "");
init_gadgets(&Nw, &po);
exiterr(!openlibs(INTUITION_LIB|GRAPHICS_LIB), "unable to open libs");
exiterr(!(Win = OpenWindow(&Nw)), "unable to open window");
Rp = Win->RPort;
SetAPen(Rp, 3);
SetDrMd(Rp, JAM2);
uparams();
precalculate();
plotcurve();
plotcontrolpts();
while (notdone) {
WaitPort(Win->UserPort);
mm = 0;
gg = 0;
while (mess = GetMsg(Win->UserPort)) {
switch(mess->Class) {
case CLOSEWINDOW:
notdone = 0;
break;
case NEWSIZE:
uparams();
precalculate();
clearwindow();
plotcurve();
plotcontrolpts();
break;
case GADGETUP:
case GADGETDOWN:
if (mess->IAddress != (APTR)&Gadgets[0]) {
do_help();
break;
}
gy = po->VertPot / 256;
gg = mess->Class;
break;
case MOUSEBUTTONS:
switch(mess->Code) {
case SELECTDOWN:
pt = findpoint(mess->MouseX, mess->MouseY);
basex = mess->MouseX;
basey = mess->MouseY;
break;
case SELECTUP:
pt = -1;
break;
case MENUDOWN:
pt = findpoint(mess->MouseX, mess->MouseY);
basex = mess->MouseX;
basey = mess->MouseY;
mrmb = 1;
break;
case MENUUP:
mrmb = 0;
pt = -1;
break;
}
break;
case MOUSEMOVE:
if (gg == GADGETDOWN) {
gy = po->VertPot / 256;
break;
}
if (pt >= 0) {
mm = 1;
mx = mess->MouseX;
my = mess->MouseY;
}
break;
default:
break;
}
ReplyMsg(mess);
}
if (gg) {
char buf[32];
mm = 0;
if (gg == GADGETUP)
gg = 0;
if (gy + 10 >= 0 && gy + 10 != SStep) {
char buf[32];
SStep = gy + 10;
TStep = SStep;
Move(Rp, 32, 16);
sprintf (buf, "Step: %-3ld", SStep);
Text(Rp, buf, strlen(buf));
precalculate();
clearwindow();
plotcurve();
plotcontrolpts();
}
}
if (mm && pt >= 0) {
if (mrmb) {
recalculate_one(pt, 0, 0, basey - my);
basey = my;
} else {
recalculate_one(pt, mx - basex, basey - my, 0);
basex = mx;
basey = my;
}
clearwindow();
plotcurve();
plotcontrolpts();
}
}
exiterr(1, NULL);
}
exiterr(n, str)
char *str;
{
if (n) {
if (str)
puts(str);
if (Win)
CloseWindow(Win);
closelibs(-1);
exit(1);
}
}
uparams()
{
Ux = Win->BorderLeft;
Uy = Win->BorderTop;
Mx = Win->Width - Win->BorderRight;
My = Win->Height- Win->BorderBottom;
}
long Pmatrix[3][4][4] = { 0,50,100,150, /* X */
0,50,100,150,
0,50,100,150,
0,50,100,150,
0, 0, 0, 0, /* Y */
16,16,16,16,
32,32,32,32,
48,48,48,48,
0,16,16, 0, /* Z */
16,32,32,16,
16,32,32,16,
0,16,16, 0
};
long Mmatrix[4][4] = { -1, 3, -3, 1,
3, -6, 3, 0,
-3, 3, 0, 0,
1, 0, 0, 0
};
long Mmatrix_trans[4][4];
long Amatrix[CSIZE][4]; /* # entries used depends on step rate */
long Bmatrix[CSIZE][4]; /* # entries used depends on step rate */
long *CXarray; /* each contains CSIZE*CSIZE*4 bytes */
long *CYarray;
long *CZarray;
short Amax, Bmax;
init_cubic()
{
register short i, j;
CXarray = (long *)malloc(CSIZE*CSIZE*4);
CYarray = (long *)malloc(CSIZE*CSIZE*4);
CZarray = (long *)malloc(CSIZE*CSIZE*4);
if (!CXarray || !CYarray || !CZarray) {
printf("unable to allocate %ld bytes\n", CSIZE*CSIZE*4*3);
return(0);
}
/* Transpose Mmatrix */
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j)
Mmatrix_trans[i][j] = Mmatrix[j][i];
}
return(1);
}
/*
* return index of point closest to the given 2D window pixel coordinate.
*
* To do this, each of the 16 control points must be transformed into their
* plot coordinates and the range to the specified window pixel taken.
* the point with the smallest range wins.
* (NOTE: Since we are using the ranges for comparison only, there is
* no need to take the square root)
*/
findpoint(wx, wy)
{
register short i, pt;
register ulong minrange = -1;
ulong range;
short x, y;
pt = 0;
for (i = 0; i < 16; ++i) {
translate(Pmatrix[0][0][i], Pmatrix[1][0][i], Pmatrix[2][0][i], &x, &y);
x -= wx;
y -= wy;
range = x*x + y*y;
if (range < minrange) {
pt = i;
minrange = range;
}
}
return(pt);
}
/*
* This need be called only when the step size changes... it calculates
* the A and B matrices from the following equation:
*
* Overall equation: C = SMPM.T. (Where X. means transpose of X)
* S = { s^3 s^2 s^1 1 } s ranging 0 to 1
* T = { t^3 t^2 t^1 1 } t ranging 0 to 1
*
* Thus, a given step size will require calculation of a certain
* number of S and T matrices. We might as well do the multiplication
* with M as well and stick the results in A and B for each step
*
* A = SM
* B = M.T.
*
* Note: As far as mmult_l() goes, any matrix with either the number
* of rows = 1 or number of columns = 1 can be transposed
* inherently (that is, it takes no work to transpose it).
*/
precalculate()
{
long Smatrix[4], Tmatrix[4];
register short s, t, i;
Smatrix[3] = Tmatrix[3] = ONE;
for (s = i = 0; s <= ONE; s += SStep) {
Smatrix[2] = s;
Smatrix[1] = (s * s) >> SSF;
Smatrix[0] = (Smatrix[1] * s) >> SSF;
mmult_l(Smatrix,Mmatrix,Amatrix[i],1,4,4);
++i;
if (s != ONE && s + SStep > ONE)
s = ONE - SStep;
}
Amax = i;
for (t = i = 0; t <= ONE; t += TStep) {
Tmatrix[2] = t;
Tmatrix[1] = (t * t) >> SSF;
Tmatrix[0] = (Tmatrix[1] * t) >> SSF;
mmult_l(Mmatrix_trans,Tmatrix,Bmatrix[i],4,4,1);
++i;
if (t != ONE && t + TStep > ONE)
t = ONE - TStep;
}
Bmax = i;
recalculate_all();
}
/*
* Recalculate all global points from the semi-compiled matrices
* A and B, and the P matrix (the 16 control points).
*
* C = APB (matrix multiplication), for each dimension. C is a 1x1
* matrix, which means that it is a simple number.
*/
recalculate_all()
{
register short t, i, s, idx;
register long *A, *B;
long C[3]; /* 3D result Coordinates */
for (s = 0; s < Amax; ++s) {
A = Amatrix[s];
idx = s * Bmax;
for (t = 0; t < Bmax; ++t, ++idx) {
B = Bmatrix[t];
for (i = 0; i < 3; ++i) {
long T[4];
mmult_l(A,Pmatrix[i],T,1,4,4);
C[i] = T[0] * B[0] +
T[1] * B[1] +
T[2] * B[2] +
T[3] * B[3];
}
CXarray[idx] = C[0];
CYarray[idx] = C[1];
CZarray[idx] = C[2];
}
}
}
/*
* At this point, we have calculated all the global points and stuck
* them in a *huge* table. If we now want to change one of the control
* points, we only have to recalculate one of the matrix elements.
* (But still have to loop through the entire grid).
*
* Note that there is a possible optimization here, since this scheme
* allows us to determine which line segments will change when we do the
* actual plotting.
*/
recalculate_one(pt, dx, dy, dz)
{
register short t, i, idx, col;
register long temp;
long arow;
short s, row;
col = pt & 3;
row = pt >> 2;
Pmatrix[0][0][pt] += dx; /* not required until a recalc_all() */
Pmatrix[1][0][pt] += dy;
Pmatrix[2][0][pt] += dz;
for (s = 0; s < Amax; ++s) {
arow = Amatrix[s][row];
idx = s * Bmax;
for (t = 0; t < Bmax; ++t, ++idx) {
temp = Bmatrix[t][col] * arow;
CXarray[idx] += temp * dx;
CYarray[idx] += temp * dy;
CZarray[idx] += temp * dz;
}
}
}
plotcurve()
{
register short i, s, t, idx;
uword x, y;
static short Corr[CSIZE][2];
for (s = 0; s < Amax; ++s) {
idx = s * Bmax;
translate(CXarray[idx]>>SSFD, CYarray[idx]>>SSFD, CZarray[idx]>>SSFD, &x, &y);
Move(Rp, x, y);
for (i = t = 0; t < Bmax; ++t, ++idx, ++i)
translate(CXarray[idx]>>SSFD, CYarray[idx]>>SSFD, CZarray[idx]>>SSFD, &Corr[i][0], &Corr[i][1]);
PolyDraw(Rp, i, Corr);
}
for (t = 0; t < Bmax; ++t) {
idx = t;
translate(CXarray[idx]>>SSFD, CYarray[idx]>>SSFD, CZarray[idx]>>SSFD, &x, &y);
Move(Rp, x, y);
for (s = i = 0; s < Amax; ++s, idx += Bmax, ++i)
translate(CXarray[idx]>>SSFD, CYarray[idx]>>SSFD, CZarray[idx]>>SSFD, &Corr[i][0], &Corr[i][1]);
PolyDraw(Rp, i, Corr);
}
}
plotcontrolpts()
{
register short i;
short x, y;
SetAPen(Rp, 1);
for (i = 0; i < 16; ++i) {
translate(Pmatrix[0][0][i], Pmatrix[1][0][i], Pmatrix[2][0][i], &x, &y);
Move(Rp, x - 2, y + 0);
Draw(Rp, x + 2, y + 0);
Move(Rp, x + 0, y - 2);
Draw(Rp, x + 0, y + 2);
}
SetAPen(Rp, 3);
}
translate(x, y, z, wx, wy)
uword *wx, *wy;
{
*wx = Ux + 50 + x + y/2;
*wy = My - 50 - (z + y/2);
}
/*
* MATRIX ROUTINES
*
* mmult_l(a,b,d,n1,n2,n3)
*
* a n1 x n2
* b n2 x n3
* d n1 x n3
*
* NOTE: This isn't the most efficient way to handle matrix multiplication.
*/
mmult_l(a,b,d,n1,n2,n3)
long *a, *b, *d;
{
register short i, j, k;
register long *an2 = a;
register long *bn3;
register long *dn3 = d;
register long sum;
for (i = 0; i < n1; ++i, dn3 += n3, an2 += n2) {
for (j = 0; j < n3; ++j) {
sum = 0;
for (k = 0, bn3 = b; k < n2; ++k, bn3 += n3)
sum += *(an2+k) * *(bn3+j);
*(dn3+j) = sum;
}
}
}
/*
* MISC
*/
clearwindow()
{
SetAPen(Rp, 0);
RectFill(Rp, Ux, Uy, Mx - 1, My - 1);
SetAPen(Rp, 3);
}
/*
* GADGET ROUTINES! ------------------------------------------------
*/
#define NG(nn) &Gadgets[nn+1]
#define G_YGLOB 1
#define G_XGLOB 2
XPI Props[] = {
{ AUTOKNOB|FREEVERT , 0, 0, 0x1FFF, 0x1FFF }
};
short pairs[] = { 0,0,10,0,10,10,0,10 };
struct Border Border[] = {
{ 0,0,1,1,JAM2,4,pairs,NULL }
};
ITEXT Itext[] = {
{ 0,1,JAM2,1,2,NULL,(ubyte *)"?",NULL }
};
IM Images[] = {
{ 0,0,2,1,1, NULL, 1, 0, NULL },
};
GADGET Gadgets[] = {
{
&Gadgets[1], -15, 22, 15, -19, GADGIMAGE|GADGHCOMP|GRELRIGHT|GRELHEIGHT,
GADGIMMEDIATE|RIGHTBORDER|RELVERIFY,PROPGADGET,
(APTR)&Images[0],NULL,NULL,0,(APTR)&Props[0], G_YGLOB, 0
},
{
NULL, -15, 11, 15, 11, GRELRIGHT,
RELVERIFY|RIGHTBORDER, BOOLGADGET,
(APTR)&Border[0],NULL,&Itext[0],0,0,0,0
}
};
GADGET *Gc;
long GUx, GUy;
init_gadgets(nw, ppo)
NW *nw;
XPI **ppo;
{
nw->FirstGadget = &Gadgets[0];
*ppo = &Props[0];
}
do_help()
{
long fh;
static char *help[] = {
"",
"(C)Copyright 1987 by Matthew Dillon, All Rights Reserved",
"Freely distributable for non-profit only",
"",
"Bezier Cubic Surfaces. Allows you to fool around with the Bezier Cubic",
"equation for 3D curved surface generation. The Bezier form uses 16",
"control points to define the surface.",
"",
"To move a control point along the Z axis, position the mouse over the",
"control point in question and move it UP or DOWN with the MENU BUTTON",
"held down. To move a control point along the X or Y axis, position the",
"mouse over the control point in question and move it LEFT/RIGHT or",
"UP/DOWN with the SELECT BUTTON held down. The Y axis is on the diagonal,",
"in a lower-left to upper-right going direction.",
"",
"The prop. Gadget is used to define the granualarity of the surface.",
"",
"(Return to continue)",
NULL
};
fh = Open("con:0/0/640/200/HELP", 1006);
if (fh) {
register short i;
char c;
for (i = 0; help[i]; ++i) {
Write(fh, help[i], strlen(help[i]));
Write(fh, "\n", 1);
}
Read(fh, &c, 1);
Close(fh);
}
}