第六章 多邊型繪圖 作者: 南台科技大學 電子系 黎靖 1. 座標系統: 2. 繪製立體圖 // viewing.cpp : viewing #include "Marco_Lee.h" #include "Graph_Lee.h" Point3 Viewing(Point3); void Coeff(float, float, float); float v11, v12, v13, v21, v22, v23, v32, v33, v43; void main() { Point3 P[] = { {100,80,50,0}, {100,0,50,1}, {100,0,0,1}, {100,80,0,1}, {0,80,0, 1}, {0,80,50,1}, {100,80,50,1}, {100,80,0,1}, {100,0,50,0}, {0,0,50,1}, {0,0,50,0}, {0,80,50,1}, {100,0,50,0}, {100,40,80,1}, {0,40,80,1}, {0,80,50,1}, {100,50,72,0}, {100,50,90,1}, {80,65, 61,1}, {80,65,90,1}, {100,65,90,0}, {80,65,90,1}, {80,65,61,1}, {100,50,72,0}, {0,0,0,1}, {0,0,0,0}, {100,80,50,1}, {100,40,80,0}, {100,65,90,1}, {80,50,90,1}, {80,50,90,0}, {80,50,72,1}, 1 {100,0,0,1}, {0,80,0,1}, {0,0,50,0}, {0,40,80,1}, {100,65,61,1}, {100,50,90,1}, {80,50,72,1}, {0,0,0,-999}}; Point3 p1, p2; float rho = 500, theta = 50, phi = 70; setWorkSpace(-100, -100, 100, 100); // x1, y1, x2, y2 setViewSpace(1, 1, 9, 7); Coeff(rho, theta, phi); setWindow(); for (int k=0; P[k].connect != -999; k++) { p2 = Viewing(P[k]); if (0 != P[k].connect) DrawLine(p1, p2, 0, black); p1 = p2; } viewWindow(); closeWindow(); } void Coeff(float rho, float theta, float phi) { double costh, sinth, cosph, sinph; costh = COS(theta); sinth = SIN(theta); cosph = COS(phi); sinph = SIN(phi); // Elements of matrix V, see Eq. (4-9): //p.127 v11 = -sinth; v12 = -cosph*costh; v13 = -sinph*costh; v21 = costh; v22 = -cosph * sinth; v23 = -sinph*sinth; v32 = sinph; v33= -cosph; v43 = rho; } /******************************************************************/ Point3 Viewing(Point3 P) // p.127 { Point3 T; // Eye coordinates, computed as in Eq. (4-2): T.x = v11*P.x + v21*P.y; T.y = v12*P.x + v22*P.y + v32*P.z; T.z = v13*P.x + v23*P.y + v33*P.z + v43; return T; } 2 3. 加入視角的考慮 // perspective.cpp : #include "Marco_Lee.h" #include "Graph_Lee.h" Point3 Perspective(Point3); void Coeff(float, float, float); float v11, v12, v13, v21, v22, v23, v32, v33, v43; float screen_dist=200, c1 = 50, c2 = -10; void main() { Point3 P[] = { {100,80,50,0}, {100,0,50,1}, {0,80,0, 1}, {0,80,50,1}, {100,0,50,0}, {0,0,50,1}, {0,0,50,0}, {0,80,50,1}, {100,0,50,0}, {100,40,80,1}, {0,40,80,1}, {0,80,50,1}, {100,50,72,0}, {100,50,90,1}, {100,0,0,1}, {100,80,50,1}, {0,0,0,1}, {0,0,0,0}, {100,80,50,1}, {100,40,80,0}, {100,65,90,1}, {100,80,0,1}, {100,80,0,1}, {100,0,0,1}, {0,80,0,1}, {0,0,50,0}, {0,40,80,1}, {100,65,61,1}, {80,65, 61,1}, {80,65,90,1}, {80,50,90,1}, {100,50,90,1}, {100,65,90,0}, {80,65,90,1}, {80,50,90,0}, {80,50,72,1}, {80,65,61,1}, {100,50,72,0}, {80,50,72,1}, {0,0,0,-999}}; Point3 p1, p2; float rho = 300, theta = 50, phi = 70; setWorkSpace(-100, -100, 100, 100); setViewSpace(1, 1, 9, 7); Coeff(rho, theta, phi); // x1, y1, x2, y2 3 setWindow(); for (int k=0; P[k].connect != -999; k++) { p2 = Perspective(P[k]); if (0 != P[k].connect) DrawLine(p1, p2, 0, black); p1 = p2; } viewWindow(); closeWindow(); } void Coeff(float rho, float theta, float phi) //p.127 { double costh, sinth, cosph, sinph; costh = COS(theta); sinth = SIN(theta); cosph = COS(phi); sinph = SIN(phi); // Elements of matrix V, see Eq. (4-9): v11 = -sinth; v12 = -cosph*costh; v13 = -sinph*costh; v21 = costh; v22 = -cosph * sinth; v23 = -sinph*sinth; v32 = sinph; v33= -cosph; v43 = rho; } /******************************************************************/ Point3 Perspective(Point3 P) // p.127 { Point3 T, Pe; // Eye coordinates, computed as in Eq. (4-2): T.x = v11*P.x + v21*P.y; T.y = v12*P.x + v22*P.y + v32*P.z; T.z = v13*P.x + v23*P.y + v33*P.z + v43; // Screen coordinates, computed as in Eqs. (4-12) and (4.13) Pe.x = screen_dist * T.x/T.z + c1; Pe.y = screen_dist * T.y/T.z + c2; return Pe; } screen_dist=500, c1 = 50, c2 = -10; rho = 400, theta = 50, phi = 70; 4 4. 隱藏線處理 (simple) // common.h #include <stdio.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #include "d_draw.h" #include "d_linesh.h" // openWindow(); viewWindow(); closeWindow(); // lineShape class #define MAX(x,y) ((x) > (y)? (x) : (y)) #define MIN(x,y) ((x) < (y)? (x) : (y)) #define MAX3(x,y,z) ((x)>(y)? MAX((x),(z)): MAX((y),(z))) #define MIN3(x,y,z) ((x)<(y)? MIN((x),(z)): MIN((y),(z))) #define SWAP(aux, a, b) { (aux) = (a); (a) = (b); (b) = (aux); } #define SWAP3(aux,a,b,c) { (aux) = (a); (a) = (b); (b) = (c); (c) = (aux); } #define SIGN(x) ((x) > eps ? 1: (x) < meps ? -1: 0) #define RD (0.0174533) // RD = 3.14159/180 #define BIG 1.e30 typedef int INDEX; typedef float COEF; typedef float DEGREE; struct POSI { float x, y, z; }; struct WorkSpace { float x1, y1, x2, y2; } WS; struct ViewSpace {float x1, y1, x2, y2;} VS; 5 POSI Viewing(POSI P); void Coeff(); DEGREE rho, theta, phi; COEF v11, v12, v13, v21, v22, v23, v32, v33, v43; /******************************************************************/ void Coeff() //p.127 { double th, ph, costh, sinth, cosph, sinph; // angles in radians: th = theta*RD; ph = phi*RD; costh = cos(th); sinth = sin(th); cosph = cos(ph); sinph = sin(ph); // Elements of matrix V, see Eq. (4-9): v11 = -sinth; v12 = -cosph*costh; v13 = -sinph*costh; v21 = costh; v22 = -cosph * sinth; v23 = -sinph*sinth; v32 = sinph; v33= -cosph; v43 = rho; } /******************************************************************/ POSI Viewing(POSI P) // p.127 { POSI T; // Eye coordinates, computed as in Eq. (4-2): T.x = v11*P.x + v21*P.y; T.y = v12*P.x + v22*P.y + v32*P.z; T.z = v13*P.x + v23*P.y + v33*P.z + v43; return T; } // Hidlin1.cpp : A simple program for hidden-line elimination // The output of this program is the file A.SCRATCH, // which is to be read by GENPLOT. #include "common.h" #define nvertex 200 // Max no of vertices #define ntriangle 200 // Max no of (no backface) triangles to be stored 6 struct Tria { int A, B, C; float a, b, c, h;} ; void ReadData(); void skipf(); int reflo(float *px); int reint(int *pi); void error(char *str); void linesegment(POSI, POSI, int); int ntr=0; float eps=1e-5, meps=-1e-5, oneminus=1-1.e-5, oneplus=1+1.e-5; POSI Vx[nvertex]; Tria TRI[ntriangle]; FILE *fin, *fout; /******************************************************************/ void main() { int i, j; fin = fopen("a.dat", "r"); fout = fopen("a.scratch","w"); if (fout == NULL) error("file a.scratch cannot be opened"); ReadData(); while (skipf(), fscanf(fin, "%d %d", &i, &j)) { linesegment(Vx[i], Vx[j], 0); } fclose(fout); } void ReadData() { int i; float r; POSI Or, P, A, B, C; Tria t; char ch; fscanf(fin, "%f %f %f", &(Or.x), &(Or.y), &(Or.z)); skipf(); fscanf(fin, "%f %f %f", &rho, &theta, &phi); Coeff(); while (skipf(), ch=getc(fin), ch != '#') { 7 ungetc(ch, fin); fscanf(fin, "%d %f %f %f", &i, &(P.x), &(P.y), &(P.z)); if (i < 0 || i >= nvertex) error("illegal vertex number"); P.x -= Or.x; P.y -= Or.y; P.z -= Or.z; Vx[i] = Viewing(P); if (Vx[i].z <= eps) { printf("Object point 0 and vertex %d lie on ", i); error("different sides of viewpoint E."); } } while (skipf(), ch=getc(fin), ch != '#') { ungetc(ch, fin); fscanf(fin, "%d %d %d", &(t.A), &(t.B), &(t.C)); A = Vx[t.A]; B = Vx[t.B]; C = Vx[t.C]; t.a = A.y *(B.z-C.z) - B.y * (A.z-C.z) + C.y*(A.z-B.z); t.b = -(A.x *(B.z-C.z) - B.x * (A.z-C.z) + C.x*(A.z-B.z)); t.c = A.x *(B.y-C.y) - B.x * (A.y-C.y) + C.x*(A.y-B.y); t.h = A.x *(B.y*C.z - C.y*B.z) - B.x *(A.y*C.z - C.y*A.z) + C.x *(A.y*B.z - B.y*A.z); if (t.h > 0) { if (ntr == ntriangle) error("Too many triangles"); r = sqrt(t.a*t.a + t.b*t.b + t.c*t.c); t.a /= r; t.b /= r; t.c /= r; t.h /= r; TRI[ntr++] = t; } } } void skipf() { while(getc(fin) != '\n');} void error(char *str) { printf("%s\n", str); exit(1); } void linesegment(POSI P, POSI Q, int k0) { // line segment PQ is to be drawn, as far as it is not hidden by the // triangles trset[k0] to trset[ntrset-1]. 8 bool worktodo = 1, Pbeyond, Qbeyond, outside, Poutside, Qoutside; POSI A, B, C, T; int k=k0, ia, ib, ic, i, sum; float a, b, c, h, hP, hQ, r1, r2, r3, dA, dB, dC, labmin, labmax, lab, mu, xmin, ymin, zmin, xmax, ymax, zmax, c1, c2, c3, k1, k2, k3, denom1, denom2, Cpos, Ppos, Qpos, aux, eps1; while (k < ntr) { a = TRI[k].a; b = TRI[k].b; c = TRI[k].c; h = TRI[k].h; eps1 = eps + eps*h; // Test 1: hP = a*P.x + b*P.y + c*P.z; hQ = a*Q.x + b*Q.y + c*Q.z; if (hP <= h+eps1 && hQ <= h+eps1) // PQ not behind ABC { k++; continue; } // Test 2: k1 = P.y*Q.z - Q.y*P.z; k3 = P.x*Q.y - Q.x*P.y; k2 = P.z*Q.x - Q.z*P.x; ia = TRI[k].A; ib = TRI[k].B; ic = TRI[k].C; A = Vx[ia]; B = Vx[ib]; C = Vx[ic]; dA = k1*A.x + k2*A.y + k3*A.z; dB = k1*B.x + k2*B.y + k3*B.z; dC = k1*C.x + k2*C.y + k3*C.z; // If dA, dB, dC have the same sign, the vertices A, B, C // lie at the same side of plane EPQ. sum = SIGN(dA)+SIGN(dB)+SIGN(dC); if (abs(sum) >= 2) { k++; continue; } // If this test succeeds, the (infinite) line PQ lies outside // pyramid EABC (or the line and the pyramid have at most one // point in common). If the test fails, there is a point of // intersection. // Test 3: Poutside = Qoutside = 0; labmin = 1.; labmax = 0.; for (i=0; i<3; i++) { 9 c1 = A.y*B.z-B.y*A.z; c2 = A.z*B.x-B.z*A.x; c3 = A.x*B.y-B.x*A.y; // c1 x + c2 y + c3 z = 0 is plane EAB Cpos = c1*C.x + c2*C.y + c3*C.z; Ppos = c1*P.x + c2*P.y + c3*P.z; Qpos = c1*Q.x + c2*Q.y + c3*Q.z; denom1 = Qpos - Ppos; if (Cpos > eps) { Pbeyond = Ppos < meps; Qbeyond = Qpos < meps; outside = (Pbeyond && Qpos<=eps) || (Qbeyond && Ppos <= eps); } else if (Cpos < meps) { Pbeyond = Ppos > eps; Qbeyond = Qpos > eps; outside = (Pbeyond && Qpos>= meps) || (Qbeyond && Ppos >= meps); } else outside = 1; if (outside) break; lab = fabs(denom1)<=eps ? 1.e7 : -Ppos/denom1; // lab indicates where PQ meets plane EAB Poutside |= Pbeyond; Qoutside |= Qbeyond; denom2 = dB-dA; mu = (fabs(denom2)<=eps ? 1.e7 : -dA/denom2); // mu tells where AB meets plane EPQ if (mu >= meps && mu <= oneplus && lab >= meps && lab <= oneplus) { labmin = MIN(lab, labmin); labmax = MAX(lab, labmax); } SWAP3(T, A, B, C); SWAP3(aux, dA, dB, dC); } if (outside) { k++; continue; } // Test 4: if (!(Poutside || Qoutside)) {worktodo = 0; break; }// PQ invisible // Test 5: 10 r1 = Q.x - P.x; r2 = Q.y - P.y; r3 = Q.z - P.z; xmin = P.x + labmin*r1; ymin = P.y + labmin*r2; zmin = P.z + labmin*r3; if (a*xmin + b*ymin + c*zmin-h < -eps1) { k++; continue; } xmax = P.x + labmax*r1; ymax = P.y + labmax*r2; zmax = P.z + labmax*r3; if (a*xmax + b*ymax + c*zmax-h < -eps1) { k++; continue; } // If this test succeeds, an intersection of PQ and the pyramid // lies in front of plane ABV. // Test 6: if (Poutside || hP < h-eps1) { T.x = xmin; T.y = ymin; linesegment(P, T, k+1); } if (Qoutside || hQ < h-eps1) { T.x = xmax; T.y = ymax; linesegment(Q, T, k+1); } worktodo = 0; T.z = zmin; T.z = zmax; break; } if (worktodo) { fprintf(fout,"%f %f 0\n", P.x/P.z, P.y/P.z); fprintf(fout,"%f %f 1\n", Q.x/Q.z, Q.y/Q.z); } } Input file : a.dat 2.5 1 1 130 7 12 8 20 70 0500 1320 2300 3302 4200 5220 6020 123 203 021 458 859 569 9 6 10 8 9 10 7 13 7 14 01 13 30 12 23 45 11 7000 8202 8 10 11 6 7 10 56 59 9222 10 0 2 2 11 0 0 2 12 7 0 0 13 0 4 0 14 0 0 3 # 10 7 11 748 7 8 11 654 647 # 89 9 10 10 11 8 11 48 6 10 # Output file : a.scratch -0.528546 -0.483811 0 0.175470 -0.024754 1 -0.278002 -0.251895 1 0.383305 -0.050821 0 0.175470 -0.024754 1 0.131256 -0.105880 0 0.142799 0.126433 1 -0.091283 0.144565 0 -0.008598 0.284395 0 -0.008309 0.182602 1 -0.278002 -0.251895 0 0.101421 -0.160621 1 0.101421 -0.160621 0 -0.147350 0.118863 1 -0.147350 0.118863 0 0.142799 0.126433 1 0.142799 0.126433 0 0.188047 0.170387 1 0.188047 0.170387 0 -0.008309 0.182602 1 -0.091283 0.144565 0 -0.008309 0.182602 1 -0.278002 -0.251895 1 0.131256 -0.105880 0 0.040236 -0.091882 1 0.131256 -0.105880 0 -0.091283 0.144565 0 -0.088381 0.052613 1 0.175470 -0.024754 0 0.188047 0.170387 1 2. GENPLOT // LeeGraph.h #include <math.h> #include "d_draw.h" #include "d_linesh.h" // openWindow(); viewWindow(); closeWindow(); // lineShape class #include "d_textsh.h" #define RD (0.0174533) #define PI (3.141592) #define ZERO 1.0E-20 #define BIG 1.e30 #define WS2VS_X(x) // RD = 3.14159/180 (((x)-WS.x1)*mfx+VS.x1) 12 #define WS2VS_Y(y) typedef float DEG; ((WS.y2-(y))*mfy+VS.y1) struct POSI { float x, y, z ; int connect;}; #define MAX(a,b) ( (a)>(b)? (a): (b)) #define MIN(a,b) ( (a)<(b)? (a): (b)) #define MAX3(x,y,z) ((x)>(y)? MAX((x),(z)): MAX((y),(z))) #define MIN3(x,y,z) ((x)<(y)? MIN((x),(z)): MIN((y),(z))) #define TOGGLE(x) ((x)? FALSE : TRUE) #define ABSVAL(val) ((val >= 0)? val: -val) /* Absolute Value Function */ #define RINGCHANGE(a,b,c) { a=b; b=c; c=a;} #define SIGN(x) ((x) > 1e-5 ? 1: (x) < -1e-5 ? -1: 0) #define SWAP(aux, a, b) { (aux) = (a); (a) = (b); (b) = (aux); } #define SWAP3(aux,a,b,c) { (aux) = (a); (a) = (b); (b) = (c); (c) = (aux); } struct WorkingSpace { float x1, y1, x2, y2; } WS; struct ViewSpace {float x1, y1, x2, y2;} VS; void setWindow(float, float, float, float); void DrawLine(POSI p1, POSI p2, shapeColor c); void moveLength(POSI p1, POSI *p2, DEG a, float L); void ShowViewCorner(); float mfx, mfy; void setWindow(float x1, float y1, float x2, float y2) { bool isotropic=1; WS.x1 = x1; WS.y1 = y1; WS.x2=x2; WS.y2=y2; VS.x1 = 1; VS.y1 = 1; VS.x2=9; VS.y2=7; openWindow(); ezdSetViewport(10,8); mfx = (VS.x2-VS.x1)/(WS.x2-WS.x1); mfy = (VS.y2-VS.y1)/(WS.y2-WS.y1); if (isotropic) mfx = mfy = (mfx < mfy? mfx: mfy); ShowViewCorner(); } void moveLength(POSI p1, POSI *p2, DEG a, float L) { p2->x = p1.x + L*cos(a*RD); p2->y = p1.y + L*sin(a*RD); DrawLine(p1, *p2, black); } 13 void DrawLine(POSI p1, POSI p2, shapeColor c) { POSI vp1, vp2; vp1.x = WS2VS_X(p1.x); vp1.y = WS2VS_Y(p1.y); vp2.x = WS2VS_X(p2.x); vp2.y = WS2VS_Y(p2.y); lineShape Line(vp1.x, vp1.y, vp2.x, vp2.y, c); Line.draw(); } void ShowViewCorner() { float epx, epy; POSI P, Q; epx = (VS.x2-VS.x1)*0.1; epy = epx*mfx/mfy; // Show the four viewport corners: P.x = Q.x = VS.x1; P.y = VS.y1; Q.y = VS.y1+epy; lineShape Line1(P.x, P.y, Q.x, Q.y, red); Line1.draw(); P.x = VS.x1; Q.y = P.y = VS.y1; Q.x = VS.x1+epx; lineShape Line2(P.x, P.y, Q.x, Q.y, red); Line2.draw(); P.x = VS.x2-epx; P.y = Q.y = VS.y1; Q.x = VS.x2; lineShape Line3(P.x, P.y, Q.x, Q.y, red); Line3.draw(); P.x = Q.x = VS.x2; P.y = VS.y1; Q.y = VS.y1+epy; lineShape Line4(P.x, P.y, Q.x, Q.y, red); Line4.draw(); P.x = Q.x = VS.x2; P.y = VS.y2-epy; Q.y = VS.y2; lineShape Line5(P.x, P.y, Q.x, Q.y, red); Line5.draw(); P.x = VS.x2-epx; P.y = Q.y = VS.y2; Q.x = VS.x2; lineShape Line6(P.x, P.y, Q.x, Q.y, red); Line6.draw(); P.x = VS.x1; P.y = Q.y = VS.y2; Q.x = VS.x1+epx; lineShape Line7(P.x, P.y, Q.x, Q.y, red); Line7.draw(); P.x = Q.x = VS.x1; P.y = VS.y2-epy; Q.y = VS.y2; lineShape Line8(P.x, P.y, Q.x, Q.y, red); Line8.draw(); } // genplot.cpp : A General adjusting and plotting program // The input is the file A.SCRATCH, #include "LeeGraph.h" void DetermineWS(); FILE *fp; void main() { POSI P, Q; 14 DetermineWS(); setWindow(WS.x1, WS.y1, WS.x2, WS.y2); fp = fopen("a.scratch", "r"); while(!feof(fp)) { fscanf(fp,"%f %f %d\n",&Q.x,&Q.y,&Q.connect); if (Q.connect) DrawLine(P,Q,black); P = Q; } fclose(fp); viewWindow(); closeWindow(); } void DetermineWS() { POSI Q; fp = fopen("a.scratch", "r"); WS.x1 = WS.y1 = BIG; WS.x2 = WS.y2 = -BIG; while(!feof(fp)) { fscanf(fp,"%f %f %d\n",&Q.x,&Q.y,&Q.connect); WS.x1 = MIN(WS.x1, Q.x); WS.x2 = MAX(WS.x2, Q.x); WS.y1 = MIN(WS.y1, Q.y); WS.y2 = MAX(WS.y2, Q.y); } fclose(fp); } 3. 隱藏線處理 (完整) // common.h #include <stdio.h> 15 #include <stdlib.h> #include <math.h> #include <ctype.h> #include "d_draw.h" #include "d_linesh.h" // openWindow(); viewWindow(); closeWindow(); // lineShape class #define MAX(x,y) ((x) > (y)? (x) : (y)) #define MIN(x,y) ((x) < (y)? (x) : (y)) #define MAX3(x,y,z) ((x)>(y)? MAX((x),(z)): MAX((y),(z))) #define MIN3(x,y,z) ((x)<(y)? MIN((x),(z)): MIN((y),(z))) #define SWAP(aux, a, b) { (aux) = (a); (a) = (b); (b) = (aux); } #define SWAP3(aux,a,b,c) { (aux) = (a); (a) = (b); (b) = (c); (c) = (aux); } #define SIGN(x) ((x) > eps ? 1: (x) < meps ? -1: 0) #define RD (0.0174533) // RD = 3.14159/180 #define BIG 1.e30 typedef int INDEX; typedef float COEF; typedef float DEGREE; struct POSI { float x, y, z; }; struct WorkSpace { float x1, y1, x2, y2; } WS; struct ViewSpace {float x1, y1, x2, y2;} VS; POSI Viewing(POSI P); void Coeff(); DEGREE rho, theta, phi; COEF v11, v12, v13, v21, v22, v23, v32, v33, v43; /******************************************************************/ void Coeff() //p.127 { double th, ph, costh, sinth, cosph, sinph; // angles in radians: th = theta*RD; ph = phi*RD; costh = cos(th); sinth = sin(th); cosph = cos(ph); sinph = sin(ph); // Elements of matrix V, see Eq. (4-9): v11 = -sinth; v12 = -cosph*costh; v13 = -sinph*costh; 16 v21 = costh; v22 = -cosph * sinth; v23 = -sinph*sinth; v32 = sinph; v33= -cosph; v43 = rho; } /******************************************************************/ POSI Viewing(POSI P) // p.127 { POSI T; // Eye coordinates, computed as in Eq. (4-2): T.x = v11*P.x + v21*P.y; T.y = v12*P.x + v22*P.y + v32*P.z; T.z = v13*P.x + v23*P.y + v33*P.z + v43; return T; } // Hidlin.cpp #include "common.h" #define XWHOLE(x) #define YWHOLE(y) #define XREAL(i) #define M -1000000.0 ((int)(((x)-WS.x1)/deltaX)) ((int)(((y)-WS.y1)/deltaY)) (WS.x1+((i))*deltaX) #define NVERTEX 1200 // Max no of vertices #define NTRIANGLE 800 // Max no of (no backface) triangles to be stored #define NSCREEN 30 #define NPOLY 400 // Max no of vertices of a single polygon #define NNTRSET 200 // Max size of set of triangles associated with // a single (long) line segment struct VERTEX { POSI p; int *connect; }; struct TRIANGLE { int A, B, C; float a, b, c, h; }; struct node { int jtr; struct node *next; } *pnode; struct { int tr_cov; float tr_dist; struct node *start; } SCREEN[NSCREEN][NSCREEN], *pointer; int comment(char ch), reint(int *pi); void rePosi(POSI *px); void skipbl(), add_linesegment(int p1, int p2), error(char *str); int counter_clock(int i0, int i1, int i2, float *pdist, int code); void linesegment(POSI P,POSI Q,int k0); void init_viewport(float, float,float,float); void ScreenConst(), DrawSegment(), AddTria2Screen(); 17 void ReadObjectStoreTriangle(), DividePolygon2Triangle(int npoly); void ReadVertex(), InitialScreenMatrix(DEGREE a,DEGREE b,DEGREE c); int POLY[NPOLY], npoly, LOWER[NSCREEN],UPPER[NSCREEN], LOW[NSCREEN], UP[NSCREEN], trset[NNTRSET], ntrset, ntr=0; float mfx, mfy, deltaX, deltaY, slope, cx, cy; const float eps=1e-5, meps=-1e-5, oneminus=1-1.e-5, oneplus=1+1.e-5; VERTEX Vx[NVERTEX]; POSI Or; TRIANGLE TRI[NTRIANGLE]; FILE *fpin, *fout; /******************************************************************/ void main() // p.121 { fpin = fopen("gen.in", "r"); fout = fopen("a.scratch","w"); if (fout == NULL) error("file a.scratch cannot be opened"); // Initialize screen matrix InitialScreenMatrix(1000, 30, 60); // (rho, theta, phi) Coeff(); openWindow(); init_viewport(0.5, 9.5, 0.5, 7.5); // Read vertices ReadVertex(); // Compute screen constants ScreenConst(); // Read object faces and store triangles ReadObjectStoreTriangle(); fclose(fpin); // Add nearest triangles to screen lists: AddTria2Screen(); // p.123 // Draw all line segments as far as they are visible DrawSegment(); viewWindow(); closeWindow(); } /******************************************************************/ void InitialScreenMatrix(DEGREE a,DEGREE b,DEGREE c) // p.121 { INDEX ipix, jpix; 18 for (ipix = 0; ipix < NSCREEN; ipix++) for (jpix = 0; jpix < NSCREEN; jpix++) { pointer = &(SCREEN[ipix][jpix]); pointer->tr_cov = -1; pointer->tr_dist = BIG; pointer->start = NULL; } rePosi(&Or); rho = a; theta = b; phi = c; } /******************************************************************/ void ReadVertex() //p.121 { INDEX i; char ch; float X, Y; POSI P, Pe; int *ptr; WS.x1 = WS.y1 = BIG; WS.x2 = WS.y2 = -BIG; // Initialize vertex array for (i = 0; i < NVERTEX; i++) Vx[i].connect=NULL; while (skipbl(), ch=getc(fpin), ch != 'F' && ch != 'f') { ungetc(ch, fpin); reint(&i); rePosi(&P); if (i < 0 || i >= NVERTEX) error("illegal vertex number"); P.x -= Or.x; P.y -= Or.y; P.z -= Or.z; Pe = Viewing(P); if (Pe.z <= eps) { printf("Object point O and vertex %d lie on ", i); error("different sides of viewpoint E."); } X = Pe.x/Pe.z; Y = Pe.y/Pe.z; WS.x1 = MIN(WS.x1, X); WS.x2 = MAX(WS.x2, X); WS.y1 = MIN(WS.y1, Y); WS.y2 = MAX(WS.y2, Y); Vx[i].p = Pe; Vx[i].connect = ptr = new int; if (ptr==NULL) error("memory allocation error 1"); *ptr = 0; } } 19 /******************************************************************/ void ScreenConst() // p. 121 { float Xcentre, Ycentre, Xvp_centre, Yvp_centre; float Xvp_range, Yvp_range, Xrange, Yrange; Xrange = WS.x2-WS.x1; Yrange = WS.y2 - WS.y1; Xvp_range = VS.x2 - VS.x1; Yvp_range = VS.y2 - VS.y1; mfx = Xvp_range/Xrange; mfy = Yvp_range/Yrange; mfx = mfy = MIN(mfx,mfy); Xcentre = 0.5*(WS.x1+WS.x2); Ycentre = 0.5*(WS.y1+WS.y2); Xvp_centre = 0.5*(VS.x1+VS.x2); Yvp_centre = 0.5*(VS.y1+VS.y2); cx = Xvp_centre - mfx*Xcentre; cy = Yvp_centre - mfy*Ycentre; deltaX = oneplus * Xrange/NSCREEN; deltaY = oneplus * Yrange/NSCREEN; // Now we have: Xrange/deltaX < NSCREEN } /******************************************************************/ void ReadObjectStoreTriangle() // p.121-122 { INDEX i, j; int vertexnr, code; float diag; char ch; while (!isspace(getc(fpin)));// The string "Faces: " has now been skipped while (reint(&i) > 0) { POLY[0] = i; npoly = 1; skipbl(); while (ch=getc(fpin), ch != '#') { ungetc(ch, fpin); reint(&POLY[npoly++]); if (npoly == NPOLY) error("too many vertices in one polygon"); } if (npoly == 1) error("only one vertex of polygon"); if (npoly == 2) { add_linesegment(POLY[0], POLY[1]); continue; } if (!counter_clock(0, 1, 2, &diag, 0)) continue; //backface for (i = 1; i <= npoly; i++) { j = i % npoly; code = POLY[j]; vertexnr = abs(code); if (Vx[vertexnr].connect == NULL) { printf("vertex %d ", vertexnr); error("undefined"); 20 } if (code <0) POLY[j] = vertexnr; else add_linesegment(POLY[i-1], vertexnr); } // Division of a polygon into triangles, see section 3-5: if (npoly > 2) DividePolygon2Triangle(npoly); } } /******************************************************************/ void DividePolygon2Triangle(int npoly) // p.122 { int i0, i1, i2, j, count=1, imin; float min_diag, diag; while (npoly > 2) { min_diag = BIG; for (i1 = 0; i1 < npoly; i1++) { i0 = (i1 == 0 ? npoly-1: i1-1); i2 = (i1 == npoly-1 ? 0 : i1+1); if (counter_clock(i0, i1, i2, &diag, 0) && diag < min_diag) { min_diag = diag; imin = i1; } } i1 = imin; i0 = (i1 == 0 ? npoly-1 : i1-1); i2 = (i1 == npoly-1 ? 0 : i1+1); // store triangle in array TRI and in screen lists: counter_clock(i0, i1, i2, &diag, count++); npoly --; for (j = i1; j <= npoly; j++) POLY[j] = POLY[j+1]; } } /******************************************************************/ void AddTria2Screen() //p.122 { INDEX ipix, jpix; for (ipix = 0; ipix < NSCREEN; ipix++) for (jpix = 0; jpix < NSCREEN; jpix++) { pointer = &(SCREEN[ipix][jpix]); if ((*pointer).tr_cov >= 0) { 21 pnode = new (struct node); if (pnode == NULL) error("memory allocation error 2"); pnode->jtr = pointer->tr_cov; pnode->next = pointer->start; pointer->start = pnode; } } } /******************************************************************/ void DrawSegment() //p.123 { INDEX p, q, iconnect, ipix, jpix, ipixleft, ipixright; int *ptr, trnr, jtr, jtop, jbot, j; POSI P, Q; float xP, yP, xQ, yQ, xLft, xRght, yLft, yRght, denom; // Draw all line segments as far as they are visible for (p = 0; p < NVERTEX; p++) { ptr = Vx[p].connect; if (ptr == NULL) continue; P = Vx[p].p; xP = P.x/P.z; yP = P.y/P.z; for (iconnect = 1; iconnect <= *ptr; iconnect ++) { q = *(ptr+iconnect); Q = Vx[q].p; xQ = Q.x/Q.z; yQ = Q.y/Q.z; // Using the screen lists, we shall build the set of // triangles that may hide points of PQ: if (xP < xQ || (xP == xQ && yP < yQ)) { xLft = xP; yLft = yP; xRght = xQ; yRght = yQ; } else {xLft = xQ; yLft = yQ; xRght = xP; yRght = yP;} ipixleft = XWHOLE(xLft); ipixright = XWHOLE(xRght); denom = xRght - xLft; if (fabs(denom) <= eps) denom = eps; slope = (yRght-yLft)/denom; jbot = jtop = YWHOLE(yLft); for (ipix = ipixleft; ipix <= ipixright; ipix++) { j = (ipix==ipixright ? YWHOLE(yRght): YWHOLE(yLft+(XREAL(ipix+1)-xLft)*slope)); LOWER[ipix] = MIN(jbot, j); jbot = j; UPPER[ipix] = MAX(jtop, j); jtop = j; 22 } ntrset = 0; for (ipix = ipixleft; ipix <= ipixright; ipix++) for (jpix = LOWER[ipix]; jpix <= UPPER[ipix]; jpix++) { pointer = &(SCREEN[ipix][jpix]); pnode = pointer->start; while(pnode != NULL) { trnr = pnode->jtr; // trnr will be store only if it is not yet // present in array trset (the triangle set) trset[ntrset] = trnr; //sentinel jtr = 0; while (trset[jtr] != trnr) jtr++; if (jtr == ntrset) { ntrset ++; // this means that trnr is stored if (ntrset == NNTRSET) error("triangle set overflow\n"); } pnode = pnode->next; } } // Now trset[0],..., trset[ntrset-1] is the set of triangles that may hide points of PQ linesegment(P, Q, 0); } } } /******************************************************************/ void skipbl() //p.124 { char ch; do ch = getc(fpin); while(isspace(ch) || comment(ch)); ungetc(ch, fpin); } /******************************************************************/ int comment(char ch) //p.124 { int k; 23 if (ch == '(') { do k = getc(fpin); return k==')'; while(k != ')' && k != EOF); } else return 0; } /******************************************************************/ void rePosi(POSI *p) //p.124 { skipbl(); fscanf(fpin, "%f %f %f", &(p->x), &(p->y), &(p->z)); } /******************************************************************/ int reint(int *p) //p.124 { skipbl(); return fscanf(fpin, "%d", p); } /******************************************************************/ void add_linesegment(int p1, int p2) // p.124 { INDEX i; int iaux, *ptr, n; if (p1 > p2) SWAP(iaux, p1, p2); // Now: p1 < p2 ptr = Vx[p1].connect; n = *ptr; for (i = 1; i <= n; i++) if (*(ptr+i) == p2) return; // p2 already in list n++; Vx[p1].connect = ptr = (int*)realloc(ptr,(n+1)*sizeof(int)); if (ptr == NULL) error("memory allocation error 3"); *(ptr+n) = p2; *ptr = n; } /******************************************************************/ int counter_clock(int i0, int i1, int i2, float *pdist, int code) //p.124-126 // code = 0: compute orientation; if counter-clockwise, // compute length of projected diagonal AC // code = 1: compute a, b, c, h; store the first triangle 24 // code > 1: check if next triangle is coplanar; store it { INDEX ipix, jpix, ipixmin, ipixmax, ipixleft, ipixright, i; int j_old, j; int A=abs(POLY[i0]), B=abs(POLY[i1]), C=abs(POLY[i2]), topcode[3]; float r, xdist, ydist, zdist, xA, yA, xB, yB, xC, yC, h0; float DA, DB, DC, D, DAB, DAC, DBC, aux, dist, xR, yR; float Xleft[3], Xright[3], Yleft[3], Yright[3]; static float a, b, c, h; POSI PA, PB, PC; float denom; PA = Vx[A].p; PB = Vx[B].p; PC = Vx[C].p; // p. 125 h0 = PA.x*(PB.y*PC.z-PC.y*PB.z) - PB.x*(PA.y*PC.z-PC.y*PA.z) + PC.x*(PA.y*PB.z-PB.y*PA.z); if (code == 0) { if(h0 > eps) { xdist = PC.x-PA.x; ydist = PC.y-PA.y; zdist = PC.z-PA.z; *pdist = xdist*xdist + ydist*ydist + zdist*zdist; return 1; } else return 0; } // If h0 = 0, plane ABC passes through E and hides nothing. // If h0 < 0, triangle ABC is a backface. // In both cases ntr is not incremented and the triangles // of the polygon are not stored if (code == 1) //p.125 { a = PA.y*(PB.z-PC.z) - PB.y*(PA.z-PC.z) + PC.y*(PA.z-PB.z); b = -(PA.x*(PB.z-PC.z) - PB.x*(PA.z-PC.z) + PC.x*(PA.z-PB.z)); c = PA.x*(PB.y-PC.y) - PB.x*(PA.y-PC.y) + PC.x*(PA.y-PB.y); r = sqrt(a*a + b*b + c*c); if(r == 0.0) r = eps; a /= r; b /= r; c /= r; h = h0/r; } else if (fabs(a*PC.x+b*PC.y+c*PC.z-h) > 0.001*fabs(h)) { 25 printf("Polygon containing vertices %d %d %d ", A, B, C); error(" incorrectly specified"); } if (ntr == NTRIANGLE) error("Too many triangles"); TRI[ntr].A = A; TRI[ntr].B = B; TRI[ntr].C = C; TRI[ntr].a = a; TRI[ntr].b = b; TRI[ntr].c = c; TRI[ntr].h = h; // The triangle will now be stored in the screen lists of the associated // pivels; first the arrays LOWER, UPPER, LOW, UP are defined: xA = PA.x/PA.z; yA = PA.y/PA.z; xB = PB.x/PB.z; yB = PB.y/PB.z; xC = PC.x/PC.z; yC = PC.y/PC.z; DA = xB*yC - xC*yB; DB = xC*yA - xA*yC; DC = xA*yB - xB*yA; D = DA+DB+DC; DAB = DC - M*(xA-xB); DAC = DB - M*(xC-xA); DBC = DA - M*(xB-xC); topcode[0] = (D*DAB>0); topcode[1] = (D*DAC>0); topcode[2] = (D*DBC>0); Xleft[0] = xA; Yleft[0] = yA; Xright[0] = xB; Yright[0] = yB; Xleft[1] = xA; Yleft[1] = yA; Xright[1] = xC; Yright[1] = yC; Xleft[2] = xB; Yleft[2] = yB; Xright[2] = xC; Yright[2] = yC; for (i = 0; i < 3; i++) // i=triangle-side number if(Xleft[i] > Xright[i] || (Xleft[i] == Xright[i] && Yleft[i] > Yright[i])) { SWAP(aux, Xleft[i], Xright[i]); SWAP(aux, Yleft[i], Yright[i]); } ipixmin = XWHOLE(MIN3(xA,xB,xC)); ipixmax = XWHOLE(MAX3(xA,xB,xC)); for (ipix = ipixmin; ipix <= ipixmax; ipix++) { LOWER[ipix] = UP[ipix] = 100000; UPPER[ipix] = LOW[ipix]=-100000; } for (i = 0; i < 3; i++) //p.126 { ipixleft = XWHOLE(Xleft[i]); ipixright = XWHOLE(Xright[i]); denom = Xright[i]-Xleft[i]; if (denom == 0.) denom = 1.0e-10; 26 slope = (Yright[i]-Yleft[i])/denom; j_old = YWHOLE(Yleft[i]); for (ipix = ipixleft; ipix <= ipixright; ipix++) { j = (ipix == ipixright ? YWHOLE(Yright[i]) : YWHOLE(Yleft[i]+(XREAL(ipix+1)-Xleft[i])*slope)); if(topcode[i]) { UPPER[ipix] = MAX3(j_old, j, UPPER[ipix]); UP[ipix] = MIN3(j_old, j, UP[ipix]); } else { LOWER[ipix] = MIN3(j_old, j, LOWER[ipix]); LOW[ipix] = MAX3(j_old, j, LOW[ipix]); } j_old = j; } } // For screen column ipix, the triangle is associated only with pixels // in the rows LOWER[ipix],...,UP[ipix]-1 of these rows denote pixels // that lie completely whithin the triangle. for (ipix = ipixmin; ipix <= ipixmax; ipix++) for (jpix = LOWER[ipix]; jpix <= UPPER[ipix]; jpix++) { pointer = &(SCREEN[ipix][jpix]); if (jpix > LOW[ipix] && jpix < UP[ipix]) { xR = WS.x1 + (ipix+0.5)*deltaX; yR = WS.y1 + (jpix+0.5)*deltaY; denom = a*xR + b*yR + c*mfx; if(fabs(denom) <= eps) denom = eps; dist = h*sqrt(xR*xR + yR*yR + 1)/denom; // This line from viewpoint E to pixel point (xR, yR, 1) // intersects plane ABC at a distance dist from E. if(dist < pointer->tr_dist) { pointer->tr_cov = ntr; pointer->tr_dist = dist;} } 27 else // Add triangle to screen list: { pnode = new (struct node); if (pnode == NULL) error("memory allocation error 4"); pnode->jtr = ntr; pnode->next = pointer->start; pointer->start = pnode; } } ntr++; } /******************************************************************/ void error(char *str) { printf("%s\n",*str); exit(1); } //p.126 /******************************************************************/ void linesegment(POSI P, POSI Q, int k0) //p.127 { // line segment PQ is to be drawn, as far as it is not hidden by the // triangles trset[k0] to trset[ntrset-1]. bool worktodo = 1, Pbeyond, Qbeyond, outside, Poutside, Qoutside; INDEX i, j, k=k0; int ia, ib, ic, sum; POSI T, A, B, C; float a, b, c, h, hP, hQ, r1, r2, r3, dA, dB, dC, labmin, labmax, lab, mu, xmin, ymin, zmin, xmax, ymax, zmax, c1, c2, c3, k1, k2, k3, denom1, denom2, Cpos, Ppos, Qpos, aux, eps1; while (k < ntrset) { j = trset[k]; a = TRI[j].a; b = TRI[j].b; c = TRI[j].c; h = TRI[j].h; // Test 1: hP = a*P.x + b*P.y + c*P.z; hQ = a*Q.x + b*Q.y + c*Q.z; eps1 = eps +eps*h; if (hP-h <= eps1 && hQ-h <= eps1) { k++; continue; } // PQ not behind ABC // Test 2: k1 = P.y*Q.z - Q.y*P.z; k2 = P.z*Q.x - Q.z*P.x; k3 = P.x*Q.y - Q.x*P.y; ia = TRI[j].A; ib = TRI[j].B; ic = TRI[j].C; A = Vx[ia].p; B = Vx[ib].p; C = Vx[ic].p; 28 dA = k1*A.x + k2*A.y + k3*A.z; dB = k1*B.x + k2*B.y + k3*B.z; dC = k1*C.x + k2*C.y + k3*C.z; // p.128 // If dA, dB, dC have the same sign, the vertices ia, ib, ic // lie at the same side of plane EPQ. sum = SIGN(dA) + SIGN(dB) + SIGN(dC); if (abs(sum) >= 2) { k++; continue; } // If this test succeeds, the (infinite) line PQ lies outside // pyramid EABC (or the line and the pyramid have at most one // point in common). If the test fails, there is a point of // intersection. // Test 3: Poutside = Qoutside = 0; labmin = 1.; labmax = 0.; for (i = 0; i < 3; i++) { c1 = A.y*B.z-B.y*A.z; c2 = A.z*B.x-B.z*A.x; c3 = A.x*B.y-B.x*A.y; // c1 x + c2 y + c3 z = 0 is plane EAB Cpos = c1*C.x + c2*C.y + c3*C.z; Ppos = c1*P.x + c2*P.y + c3*P.z; Qpos = c1*Q.x + c2*Q.y + c3*Q.z; denom1 = Qpos - Ppos; if (Cpos > eps) { Pbeyond = Ppos < meps; Qbeyond = Qpos < meps; outside = (Pbeyond && Qpos <= eps) || (Qbeyond && Ppos <= eps); } else if (Cpos < meps) { Pbeyond = Ppos > eps; Qbeyond = Qpos > eps; outside = (Pbeyond && Qpos>= meps) || (Qbeyond && Ppos >= meps); } else outside = 1; if (outside) break; lab = (fabs(denom1)<=eps ? 1.e7 : -Ppos/denom1); // lab indicates where PQ meets plane EAB Poutside |= Pbeyond; Qoutside |= Qbeyond; denom2 = dB-dA; mu = (fabs(denom2)<=eps ? 1.e7 : -dA/denom2); 29 // mu tells where AB meets plane EPQ if (mu >= meps && mu <= oneplus && lab >= meps && lab <= oneplus) { labmin = MIN(lab, labmin); labmax = MAX(lab, labmax); } SWAP3(T, A, B, C); SWAP3(aux, dA, dB, dC); } if (outside) { k++; continue; } // Test 4: if (!(Poutside || Qoutside)) {worktodo = 0; break; }// PQ invisible // p.129 Test 5: r1 = Q.x-P.x; r2 = Q.y-P.y; r3 = Q.z-P.z; xmin = P.x+labmin*r1; ymin = P.y+labmin*r2; zmin = P.z+labmin*r3; if (a*xmin+b*ymin+c*zmin-h < -eps1) { k++; continue; } xmax = P.x+labmax*r1; ymax = P.y+labmax*r2; zmax = P.z+labmax*r3; if (a*xmax+b*ymax+c*zmax-h < -eps1) { k++; continue; } // If this test succeeds, an intersection of PQ and the pyramid // lies in front of plane ABV. // Test 6: if (Poutside || hP < h-eps1) { T.x = xmin; T.y = ymin; T.z = zmin; linesegment(P, T, k+1); } if (Qoutside || hQ < h-eps1) { T.x = xmax; T.y = ymax; T.z = zmax; linesegment(Q, T, k+1); } worktodo = 0; break; } if (worktodo) { lineShape Line(mfx*P.x/P.z+cx, 8-mfy*P.y/P.z-cy, mfx*Q.x/Q.z+cx, 8-mfy*Q.y/Q.z-cy, black); Line.draw(); fprintf(fout,"%f %f 0\n",P.x/P.z, P.y/P.z); fprintf(fout,"%f %f 1\n",Q.x/Q.z, Q.y/Q.z); } 30 } /******************************************************************/ void init_viewport(float x1, float x2, float y1, float y2) { float len = 0.2; VS.x1 = x1; VS.x2 = x2; VS.y1 = y1; VS.y2 = y2; // Show the four viewport corners: lineShape Line1(x1, y1, x1, y1+len, red); Line1.draw(); lineShape Line2(x1, y1, x1+len, y1, red); Line2.draw(); lineShape Line3(x2-len, y1, x2, y1, red); Line3.draw(); lineShape Line4(x2, y1, x2, y1+len, red); lineShape Line5(x2, y2-len, x2, y2, red); lineShape Line6(x2-len, y2, x2, y2, red); lineShape Line7(x1, y2, x1+len, y2, red); lineShape Line8(x1, y2-len, x1, y2, red); ezdDrawPoint((x1+x2)/2, y1); Line4.draw(); Line5.draw(); Line6.draw(); Line7.draw(); Line8.draw(); } /******************************************************************/ // a.dat 0 0 30 0 0 -30 0 10 -10 -30 0 1 0 -20 0 11 -10 -20 0 2 0 -16 8 12 -10 -16 8 3 0 16 8 13 -10 16 8 4 0 20 0 14 -10 20 0 5 0 30 0 15 -10 30 0 6 0 0 60 16 -10 0 60 7 0 -12 16 17 -10 -12 16 8 0 12 16 18 -10 12 16 9 0 0 40 31 19 -10 Faces: 0 40 0 1 2 3 4 5 6 -9 8 7 9 -6# 10 16 -19 17 18 19 -16 15 14 13 12 11# 1 11 12 2# 2 12 13 3# 14 4 3 13# 7 8 18 17# 7 17 19 9# 18 8 9 19# 5 15 16 6# 10 0 10 11 14 15 6 16# 1 0# 5 4# // rho = 70, theta = 20, phi = 80 5. 例 1:Lettersa // lettersa.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #define thickness 10 #define s(p) ( (p) >= 0 ? (p)+base : (p) - base) typedef struct Point3Struct {float x, y, z; } Point3; #define point(i,P) fprintf(fp, "%d %f %f %f\n",i,P.x,P.y,P.z) #define face4(a,b,c,d) fprintf(fp,"%4d %4d %4d %4d#\n",s(a),s(b),s(c),s(d)) #define face12(a,b,c,d,e,f,g,h,i,j,k,l) \ fprintf(fp, "%4d %4d %4d %4d %4d %4d %4d %4d %4d %4d %4d %4d#\n",\ 32 s(a), s(b), s(c), s(d), s(e), s(f), s(g), s(h), s(i), \ s(j), s(k), s(l)) int base; FILE *fp; void main() { int n, i, j, nr; static Point3 Q, P[20]= { {0, -30, 0}, {0, -20, 0} , {0, -16, 8}, {0, 16, 8}, {0, 20, 0}, { 0, 30, 0}, {0, 0, 60}, {0, -12, 16}, {0, 12, 16}, {0, 0, 40}}; printf("How many letters?\n"); scanf("%d",&n); fp = fopen("a.dat","w"); fprintf(fp,"%f %f %f\n", -(n-0.5)*thickness, 0.0, 30.0); for (j = 10; j < 20; j++) { P[j].x=-thickness; P[j].y = P[j-10].y; P[j].z = P[j-10].z; for (i = 0; i < n; i++) for (j = 0; j < 20; j++) { nr = 20*i+j; Q = P[j]; Q.x = P[j].x-2*i*thickness; point(nr,Q); } fprintf(fp,"Faces:\n"); for (i = 0; i < n; i++) { base = 20*i; face12( 0, 1, 2, 3, 4, 5, 6, -9, 8, 7, 9, -6); face12(10, 16, -19, 17, 18, 19, -16, 15, 14, 13, 12, 11); face4( 1, 11, 12, 2); face4( 2, 12, 13, 3); face4(14, 4, 3, 13); face4( 7, 8, 18, 17); face4( 7, 17, 19, 9); face4( 5, 15, 16, 6); face4(10, 11, 1, 0); face4(18, 8, 9, 19); face4(10, 0, 6, 16); face4(14, 15, 5, 4); } fclose(fp); } // rho = 700, theta = 80, phi = 80 33 } 6. 例 2: //hollow.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #include <math.h> #define PI (3.14159) void main() { FILE *fp; int i, j, k, l, m, n; float r, R, alpha, cosa, sina, delta, h, radius, hite; printf("Give number n (n points on a circle):"); scanf("%d", &n); printf("Give cylinder height:"); scanf("%f",&h); printf("Give large radius R and small r:\n"); scanf("%f %f", &R, &r); fp = fopen("cyl.dat", "w"); delta = 2.0*PI/n; fprintf(fp,"0.0 0.0 %7.2f\n", 0.5*h); for (i=1; i <= n; i++) { alpha = i*delta; cosa = cos(alpha); sina = sin(alpha); for (l = 0; l < 2; l++) // l=0: outer, l=1: inner circle { radius = (l == 0 ? R : r); for (m = 0; m < 2; m++) // m=0: top, m=1: bottom boundary { 34 k = i + l*n + m*2*n; hite = (m==0? h : 0); fprintf(fp,"%d %9.5f %9.5f %9.5f\n", k, radius*cosa, radius*sina, hite); } } } fprintf(fp, "Faces:\n"); // Top boundary face: for (i=1; i<=n; i++) fprintf(fp,"%d ",i); fprintf(fp,"%d\n", -2*n); for(i=2*n-1; i>=n+1;i--) fprintf(fp, "%d ",i); fprintf(fp,"%d %d#\n", 2*n, -n); // Bottom boundary face: fprintf(fp, "%d %d\n", 3*n, -4*n); for (i=3*n+1; i<=4*n; i++) fprintf(fp,"%d ",i); fprintf(fp,"%d\n",-3*n); for (i=3*n-1; i >= 2*n+2; i--) fprintf(fp,"%d ",i); fprintf(fp,"%d#\n", 2*n+1); // Vertical lines: for (i = 1; i <= n; i++) { j=i%n+1; fprintf(fp, "%d %d %d %d#\n",j,i,i+2*n,j+2*n); fprintf(fp, "%d %d %d %d#\n",i+n, j+n, j+3*n, i+3*n); } fclose(fp); } N = 10, h=10, R=30, r=25 // rho = 1700, theta = 20, phi = 60 35 N = 100, h=10, R=30, r=25 // rho = 1700, theta = 20, phi = 60 7. 例 3 //beams.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #define point(i,P) fprintf(fp, "%d %f %f %f\n",i,P.x,P.y,P.z) #define face4(a,b,c,d) fprintf(fp,"%d %d %d %d#\n",a,b,c,d) void main() { struct POSI {float x, y, z;} A, B, C, D; FILE *fp; int i, j, n, k; float l, w, a, b, aux; printf("How many beams?\n"); scanf("%d", &n); printf("The beam measures l x w x w. \nGive l and w: "); scanf("%f %f",&l, &w); fp = fopen("beams.dat", "w"); fprintf(fp, "0.0 0.0 %f\n", 0.5*n*w); // central object point 36 a = 0.5*l; b = a-w; A.x = a; A.y = -a; B.x = a; B.y = a; C.x = b; C.y = a; D.x = b; D.y = -a; for(i = 0; i < n; i++) { for(j = 0; j < 2; j++) { A.z = B.z = C.z = D.z = (i+j)*w; k = 8*i + 4*j; point(k, A); point(k+1, B); point(k+2, C); point(k+3, D); } aux = A.x; A.x = -A.y; A.y = aux; aux = B.x; B.x = -B.y; B.y = aux; aux = C.x; C.x = -C.y; C.y = aux; aux = D.x, D.x = -D.y; D.y = aux; } fprintf(fp, "Faces:\n"); for (i = 0; i < n; i++) { k = 8*i; face4(k, k+3, k+2, k+1); // bottom face4(k+4, k+5, k+6, k+7); // top face4(k, k+1, k+5, k+4); face4(k+3, k+7, k+6, k+2); face4(k, k+4, k+7, k+3); face4(k+1, k+2, k+6, k+5); // front // bkck // left // right } fclose(fp); } 37 8. 例 4 //staircase.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #include <math.h> #define PI (3.14159) #define point(i,P) fprintf(fp, "%d %f %f %f\n",i,P.x,P.y,P.z) #define face4(a,b,c,d) fprintf(fp,"%d %d %d %d#\n",a,b,c,d) void main() { FILE *fp; int i, j, k, l, m, n; float r, R, alpha, cosa, sina, delta, h, H; struct POSI {float x, y, z;} A, P[10]; printf("Give number n (n points on a stairs):"); scanf("%d", &n); printf("Give height of a single stair step:"); scanf("%f",&h); printf("Give large radius R and small r:\n"); scanf("%f %f", &R, &r); fp = fopen("winding.dat", "w"); delta = 2.0*PI/n; fprintf(fp,"0.0 0.0 %7.2f\n", 0.5*n*h); for (j=0; j<4; j++) P[j].z = 0; for (j=4; j<8; j++) P[j].z = h/5; P[0].x = P[1].x = P[4].x = P[5].x = R; P[2].x = P[3].x = P[6].x = P[7].x = r; P[0].y = P[4].y = P[3].y = P[7].y = -0.75*h; 38 P[1].y = P[5].y = P[2].y = P[6].y = 0.75*h; P[8].x = R; P[8].y = 0; P[8].z = h/10; P[9].x = R; P[9].y = 0; P[9].z = 5*h; m = 10*n; H = n*h; for (i = 0; i < n; i++) { alpha = i*delta; cosa = cos(alpha); sina = sin(alpha); for (j=0; j<10; j++) { k = 10*i+j; A.x = P[j].x*cosa - P[j].y*sina; A.y = P[j].x*sina + P[j].y*cosa; A.z = P[j].z + i*h; point(k, A); } A.x = r*cosa; A.y = r*sina; A.z = 0.0; point(m+i, A); A.z = H+5*h; point(m+n+i, A); } fprintf(fp, "Faces:\n"); for (i = 0; i < n; i++) { k = 10*i; face4(k , k+1, k+5, k+4); face4(k+2, k+3, k+7, k+6); face4(k+1, k+2, k+6, k+5); face4(k+3, k , k+4, k+7); face4(k+4, k+5, k+6, k+7); face4(k+1, k, k+3, k+2); fprintf(fp, "%d %d#\n", k+8, k+9); if (i < n-1) fprintf(fp, "%d %d#\n",k+9, k+19); } for (i=0; i<n; i++) for (l=0; l<2; l++) face4(m+i, m+(i+1)%n, m+n+(i+1)%n, m+n+i); { for (i=0; i<n; i++) fprintf(fp, " %d", m+l*n+i); fprintf(fp,"#\n"); } fclose(fp); } 39 9. 例 5 //torus.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #include <math.h> #define PI (3.14159) void main() { FILE *fp; int i, j, n; float r, R, alpha, beta, cosa, sina, delta, x, x1, y1, z1; printf("Give number n (to draw an n x n torus):"); scanf("%d", &n); printf("Give large radius R and small r:\n"); scanf("%f %f", &R, &r); fp = fopen("torus.dat", "w"); delta = 2.0*PI/n; fprintf(fp,"0.0 0.0 0.0\n"); // central object point for (i=0; i<n; i++) { alpha = i*delta; cosa = cos(alpha); sina = sin(alpha); for (j=0; j<n; j++) { beta = j*delta; x = R + r*cos(beta); // y=0 x1 = x*cosa; y1 = x*sina; z1 = r*sin(beta); //z1 = z; fprintf(fp, "%d %f %f %f\n", i*n+j, x1, y1, z1); } 40 } fprintf(fp, "Faces:\n"); for (i = 0; i < n; i++) for (j = 0; j < n; j++) fprintf(fp, "%d %d %d %d#\n",i*n+j,(i+1)%n*n+j, (i+1)%n*n+(j+1)%n, i*n+(j+1)%n); fclose(fp); } 10. 例 6 //sphere.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #include <math.h> #define PI (3.14159) void main() { FILE *fp; int i, j, n, A, B, C, D, P, Q; float alpha, beta, delta, cosa, sina, cosb, sinb; printf("Give number n (to draw an n x n semi-sphere):"); scanf("%d", &n); fp = fopen("semi.dat", "w"); delta = 0.5*PI/n; fprintf(fp,"0.0 0.0 -0.5\n"); // central object point // R = 1; sphere centre in 0 41 fprintf(fp,"0 0.0 0.0 -1.0\n"); // first point for (i=0; i<4*n; i++) { alpha = i*delta; cosa = cos(alpha); sina = sin(alpha); for (j=1; j<=n; j++) { beta = j*delta; cosb = cos(beta); sinb = sin(beta); fprintf(fp, "%d %f %f %f\n", i*n+j, sinb*cosa, sinb*sina, -cosb); } } fprintf(fp, "Faces:\n"); for (i = 0; i < 4*n; i++) { P=i*n+1; Q=(i+1)%(4*n)*n+1; fprintf(fp,"%d %d %d#\n",0, P, Q); fprintf(fp,"%d %d %d#\n",0, Q, P); for (j = 1; j < n; j++) { A=P+j-1; B=Q+j-1; C=Q+j; D=P+j; fprintf(fp, "%d %d %d %d#\n",A,B,C,D); fprintf(fp, "%d %d %d %d#\n",D,C,B,A); } } fclose(fp); } x2+y2+z2=1, -1<=z<=0 42 11. 例 7 //function.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> typedef float COEFF; #define point(i,x,y,z) fprintf(fp, "%d %f %f %f\n",i,x,y,z) #define face3(a,b,c) fprintf(fp,"%d %d %d#\n",a,b,c) #define line(a,b) fprintf(fp,"%d %d#\n",a,b) float f(float x, float y); void InputPolynomial(); void SetRange(), SetGrid(), OutputData(); FILE *fp= fopen("func.dat", "w"); COEFF a, b, c, d, e, g; float xmin, xmax, ymin, ymax, hx, hy, x, y, zc, xaxis, yaxis, zaxis; int Nx, Ny; void main() { InputPolynomial(); SetRange(); SetGrid(); OutputData(); fclose(fp); } float f(float x, float y) { return a*x*x+b*y*y+c*x*y+d*x+e*y+g;} void InputPolynomial() { printf("f(x,y)=a.x.x + b.y.y + c.x.y + d.x + e.y +g\n"); printf("a, b, c, d, e, g: "); scanf("%f %f %f %f %f %f", &a, &b, &c, &d, &e, &g); } void SetRange() { printf("xmin, xmax, ymin, ymax: "); scanf("%f %f %f %f",&xmin, &xmax, &ymin, &ymax); printf("Central z-value: "); scanf("%f",&zc); printf("Length of positive axes to be drawn (x,y,z): "); 43 scanf("%f %f %f", &xaxis, &yaxis, &zaxis); x = (xmin+xmax)/2; y=(ymin+ymax)/2; } void SetGrid() { printf("Nx, Ny: "); scanf("%d %d", &Nx, &Ny); hx = (xmax-xmin)/Nx; hy = (ymax-ymin)/Ny; } void OutputData() { int i, j, k, l; fprintf(fp, "%f %f %f\n", x,y,zc); for(i=0; i<=Nx; i++) for(j=0; j<=Ny; j++) { x = xmin + i*hx; y = ymin + j*hy; point(j*(Nx+1)+i+1, x, y, f(x,y)); } k = (Nx+1)*(Ny+1); point(++k, 0.0, 0.0, 0.0); point(++k, xaxis, 0.0, 0.0); point(++k, 0.0, yaxis, 0.0); point(++k, 0.0, 0.0, zaxis); fprintf(fp,"Faces:\n"); for(i = 0; i < Nx; i++) for(j = 0; j < Ny; j++) { k=j*(Nx+1)+i+1; l = k+Nx+1; face3(k, -(l+1), k+1); face3(k+1, l+1, -k); face3(k, -(l+1), l); face3(l, l+1, -k); } k = (Nx+1)*(Ny+1); line(k+1, k+2); // x-axis line(k+1, k+3); // y-axis line(k+1, k+4); // z-axis } F(x, y) = 0.1x2-0.4y2 -5 ≦ x ≦ 5, -2 ≦ y ≦ 2 Nx = 20, Ny=8, zc = 0, (x,y,z): (5, 5, 5) rho = 20, theta = 50, phi = 80 44 12. 例 8 //fun2.cpp: A preprocessor for Hidlin.cpp #include <stdio.h> #include <math.h> float f(float x, float y); void InputPolynomial(); void SetRange(); void SetGrid(); void OutputData(); FILE *fp= fopen("func.dat", "w"); float xmin, xmax, ymin, ymax, hx, hy, x, y, zc, xaxis, yaxis, zaxis; int Nx, Ny; void main() { SetRange(); SetGrid(); OutputData(); fclose(fp); } float f(float x, float y) { 45 float RD = 0.0174533; return (70*cos(sqrt(x*x+y*y)*RD)); } void SetRange() { printf("xmin, xmax, ymin, ymax: "); scanf("%f %f %f %f",&xmin, &xmax, &ymin, &ymax); printf("Central z-value: "); scanf("%f",&zc); printf("Length of positive axes to be drawn (x,y,z): "); scanf("%f %f %f", &xaxis, &yaxis, &zaxis); x = (xmin+xmax)/2; y=(ymin+ymax)/2; } void SetGrid() { printf("Nx, Ny: "); scanf("%d %d", &Nx, &Ny); hx = (xmax-xmin)/Nx; hy = (ymax-ymin)/Ny; } void OutputData() { int i, j, k, l; fprintf(fp, "%f %f %f\n", x,y,zc); for(i=0; i<=Nx; i++) for(j=0; j<=Ny; j++) { x = xmin + i*hx; y = ymin + j*hy; fprintf(fp, "%d %f %f %f\n", j*(Nx+1)+i+1, x, y, f(x,y)); } k = (Nx+1)*(Ny+1); fprintf(fp,"%d %f %f %f\n", ++k, 0.0, 0.0, 0.0); fprintf(fp,"%d %f %f %f\n", ++k, xaxis, 0.0, 0.0); fprintf(fp,"%d %f %f %f\n", ++k, 0.0, yaxis, 0.0); fprintf(fp,"%d %f %f %f\n", ++k, 0.0, 0.0, zaxis); fprintf(fp,"Faces:\n"); for(i = 0; i < Nx; i++) for(j = 0; j < Ny; j++) { 46 k=j*(Nx+1)+i+1; l = k+Nx+1; fprintf(fp,"%d %d %d#\n", k, -(l+1), k+1); fprintf(fp,"%d %d %d#\n", k+1, l+1, -k); fprintf(fp,"%d %d %d#\n", k, -(l+1), l); fprintf(fp,"%d %d %d#\n", l, l+1, -k); } k = (Nx+1)*(Ny+1); fprintf(fp,"%d %d#\n", k+1, k+2); // x-axis fprintf(fp,"%d %d#\n", k+1, k+3); // y-axis fprintf(fp,"%d %d#\n", k+1, k+4); // z-axis } F(x, y) = 70 cos sqrt ( x 2 y 2 ) 180 -500 ≦ x ≦ 500, -500 ≦ y ≦ 500 Nx = 20, Ny=20, zc = 0, (x,y,z): (600, 600, 100) rho = 1000, theta = 20, phi = 60 13. Cube // cube.dat 000 1000 47 2100 3110 4010 5001 6101 7111 8011 Faces: 2 3 7 6# 4 1 5 8# 3 4 8 7# 1 2 6 5# 6 7 8 5# 3 2 1 4# 14. Cylinder // Cylinder.cpp Program to generate a cylinder #include <stdio.h> #include <math.h> #include <ctype.h> #include <process.h> void main() { FILE *fp; char ch, str[50]; 48 float diam, r, h, delta, alpha, pi, rcos, rsin, half; int i, i1, n; pi = 4*atan(1.0); fp = fopen("cylin.dat", "w"); do { printf("Direction of axis? (X/Y/Z) "); ch = getchar(); ch = toupper(ch); } while (ch != 'X' && ch != 'Y' && ch != 'Z'); printf("Diameter? "); /* scanf("%f", &diam); r = diam/2; printf("Altitude? "); scanf("%f",&h); half = h/2; printf("Number of polygon edges? "); scanf("%d", &n); printf("Name of output file?"); scanf("%s", str); fp = fopen(str, "w"); */ if (fp == NULL) { printf("File problem"); exit(1); } delta = 2*pi/n; fprintf(fp, "%f %f %f\n", 0, 0, -half); for (i=1; i <= n; i++) { alpha = i*delta; rcos = r*cos(alpha); rsin = r*sin(alpha); if (ch == 'Z') { fprintf(fp, "%d %f %f %f\n", i, rcos, rsin, half); fprintf(fp, "%d %f %f %f\n", i+n, rcos, rsin, -half); } else { if (ch == 'X') { fprintf(fp, "%d %f %f %f\n", i, half, rcos, rsin); fprintf(fp, "%d %f %f %f\n", i+n, -half, rcos, rsin); } else // ch == 'Y' 49 { fprintf(fp, "%d %f %f %f\n", i, rsin, half, rcos); fprintf(fp, "%d %f %f %f\n", i+n, rsin, -half, rcos); } } } fprintf(fp, "Faces:\n"); for (i=1; i<=n; i++) fprintf(fp, "%d%s", i, (i==n? "#\n" : i%10? " ": "\n")); for (i=1; i<=n; i++) fprintf(fp, "%d%s", 2*n+1-i, (i==n? "#\n" : i%10? " ": "\n")); for (i=1; i<=n; i++) { i1 = (i==n ? 1: i+1); fprintf(fp, "%d %d %d %d#\n", i, i+n, i1+n, i1); } fclose(fp); } Z, 10, 15, 4 Z, 10, 15, 10 50 Z, 10, 15, 50 15. Cones and pyramids // Cone.cpp Program to generate a cone #include <stdio.h> #include <math.h> #include <process.h> void main() { FILE *fp; 51 char ch, str[50]; float diam, r, h, delta, alpha, x, y, pi; int i, n; pi = 4*atan(1.0); fp = fopen("Cone.dat", "w"); printf("Diameter of the base circle? "); scanf("%f", &diam); r = diam/2; printf("Altitude? "); scanf("%f",&h); printf("Number of polygon edges? "); scanf("%d", &n); /* printf("Name of output file?"); scanf("%s", str); fp = fopen(str, "w"); */ if (fp == NULL) { printf("File problem"); exit(1); } delta = 2*pi/n; fprintf(fp, "%f %f %f\n", 0., 0., 0.); for (i=1; i <= n; i++) { alpha = i*delta; x = r*cos(alpha); y = r*sin(alpha); fprintf(fp, "%2d %f %f %f\n", i, x, y, 0.); } fprintf(fp, "%2d %f %f %f\n", n+1, 0., 0., h); fprintf(fp, "Faces:\n"); for (i=n; i>=1; i--) fprintf(fp,"%3d%s", i, (i==1? "#\n" : (i%10 == 1 ? "\n" : ""))); for (i=1; i<=n; i++) fprintf(fp, "%2d %2d %2d#\n", i, (i==n? 1 : i+1), n+1); fclose(fp); } 52 16. Traditional approximation of a sphere // Sphere.cpp Sphere approximation with m slices, n points #include <stdio.h> #include <math.h> #include <process.h> void main() { FILE *fp; char ch, str[50]; double r, theta, delphi, deltheta, rsinphi, rcosphi, x, y, z, pi, phi; int i, j, m, n, nr, next, southpole; /* pi = 4*atan(1.0); fp = fopen("Sphere.dat", "w"); printf("Enter m, the number of horizontal slices: "); scanf("%d", &m); printf("\nThere will be n points on each horizontal circle.\n"); printf("It is recommended to choose n about twice as large as m.\n"); printf("Enter n: "); scanf("%d", &n); delphi = pi/m; deltheta = 2*pi/n; printf("Radius of the sphere: "); scanf("%lf", &r); printf("Name of output file?"); scanf("%s", str); fp = fopen(str, "w"); */ if (fp == NULL) { printf("File problem"); exit(1); } fprintf(fp, "%f %f %f\n", 0., 0., 0.); // Vertex numbering: // i = 0: 1 // i = 1: 2, 3, ..., n+1 // i = 2: n+2, n+3, ..., 2n+1 // ... 53 // // i = m-1: (m-2)n+2, (m-2)n+3, ..., (m-1)n+1 i = m: (m-1)n+2 fprintf(fp, "%d %f %f %f\n", 1, 0., 0., r); // i=0 for (i=1; i<m; i++) { phi = i*delphi; rcosphi = r*cos(phi); rsinphi = r*sin(phi); for (j=0; j<n; j++) { nr = (i-1)*n+j+2; theta = j*deltheta; x = rsinphi * cos(theta); y = rsinphi * sin(theta); z = rcosphi; fprintf(fp,"%d %f %f %f\n", nr, x, y, z); } } fprintf(fp, "%d %f %f %f\n", (m-1)*n+2, 0., 0., -r); // i=m fprintf(fp, "Faces:\n"); for (j=2; j<=n+1; j++) fprintf(fp, "%d %d %d#\n", 1, j, (j<n+1? j+1 : 2)); for (i=1; i<m-1; i++) { nr = (i-1)*n; for (j=2; j<=n+1; j++) { next = (j<n+1? j+1: 2); fprintf(fp, "%d %d %d %d#\n", nr+j, nr+n+j, nr+n+next, nr+next); } } southpole = (m-1)*n+2; nr = (m-2)*n; for (j=2; j<=n+1; j++) fprintf(fp, "%d %d %d#\n",nr+j, southpole, (j<n+1? nr+j+1: nr+2)); fclose(fp); } m = 4, n= 8 54 m=10, n=20 17. Octahedron //Octah.dat 0 0 -1 1 0.5 0.5 0. 2 -0.5 0.5 0. 3 -0.5 -0.5 0. 4 0.5 -0.5 0. 5 0. 0. 0.707107 6 0. 0. -0.707107 Faces: 1 2 5# 2 3 5# 3 4 5# 4 1 5# 55 1 6 2# 2 6 3# 3 6 4# 4 6 1# 18. Dodecahedron // Dodeca.cpp This program constructs a dodecahedron and a cube in which it fits. #include <stdio.h> #include <math.h> #include <ctype.h> #include <process.h> void main() { FILE *fp; char ch, str[50]; float s, a, h, v, d, tau, pi; int i; static float x[21], y[21], z[21]; // initially 0 pi = 4*atan(1.0); fp = fopen("Dodeca.dat", "w"); tau = (sqrt(5.0)+1)/2; // tau = 2*cos(pi/5) // sometimes the letter phi is used instead of tau s = sqrt(3-tau)/2; // s = sin(pi/5) a = tau * s; h = (2*s + (tau+1)*sqrt(tau+2))/4; // Half edge of cube v = (tau-1) * h; d = (tau-1) *(h-s); printf("The twelve faces of the dodecahedron are pentagons,\n"); printf("each fitting in a circle with radius 1.\n"); printf("The sides of these pentagons have length %f.\n", 2*s); printf("The dodecahedron fits in a cube with edges of length"); printf(" %f.\n", 2*h); x[5] = x[7] = s; x[6] = x[8] = -s; x[9] = x[10] = h; x[11] = x[12] = -h; x[13] = x[15] = x[17] = x[19] = a; x[14] = x[16] = x[18] = x[20] = -a; y[1] = y[3] = -s; y[2] = y[4] = s; y[5] = y[6] = h; y[7] = y[8] = -h; y[13] = y[14] = y[17] = y[18] = h-d; 56 y[15] = y[16] = y[19] = y[20] = -(h-d); z[1] = z[2] = h; z[3] = z[4] = -h; z[9] = z[11] = s; z[10] = z[12] = -s; z[13] = z[14] = z[15] = z[16] = v; z[17] = z[18] = z[19] = z[20] = -v; fp = fopen("dodeca.dat", "w"); fprintf(fp, "%f %f %f\n", 0., 0., 0.); for (i=1; i<=20; i++) fprintf(fp, "%3d %f %f %f\n", i, x[i], y[i], z[i]); // The cube in which the dodecahedron fits will have the vertex // numbers 101, 102, ..., 108: fprintf(fp, "101 %f %f %f\n", h, -h, -h); fprintf(fp, "102 %f %f %f\n", h, h, -h); fprintf(fp, "103 %f %f %f\n", -h, h, -h); fprintf(fp, "104 %f %f %f\n", -h, -h, -h); fprintf(fp, "105 %f %f %f\n", h, -h, h); fprintf(fp, "106 %f %f %f\n", h, h, h); fprintf(fp, "107 %f %f %f\n", -h, h, h); fprintf(fp, "108 %f %f %f\n", -h, -h, h); fprintf(fp, "Faces:\n"); fprintf(fp," 1 15 9 13 2#\n"); fprintf(fp," 1 2 14 11 16#\n"); fprintf(fp," 5 6 14 2 13#\n"); fprintf(fp," 7 15 1 16 8#\n"); fprintf(fp," 19 10 9 15 7#\n"); fprintf(fp," 10 17 5 13 9#\n"); fprintf(fp," 20 8 16 11 12#\n"); fprintf(fp," 18 12 11 14 6#\n"); fprintf(fp," 3 4 17 10 19#\n"); fprintf(fp," 4 18 6 5 17#\n"); fprintf(fp," 3 20 12 18 4#\n"); fprintf(fp," 3 19 7 8 20#\n"); // The cube edges are entered as loose line segments: fprintf(fp, "101 102#\n102 103#\n103 104#\n104 101#\n"); fprintf(fp, "105 106#\n106 107#\n107 108#\n108 105#\n"); fprintf(fp, "101 105#\n102 106#\n103 107#\n104 108#\n"); fclose(fp); } 57 19. Icosahedron // Icosa.cpp Icosahedron: a regular polyhedron with 20 boundary faces, // which are equilateral triangules. #include <stdio.h> #include <math.h> #define point(i,x,y,z) fprintf(fp, "%d %f %f %f\n",i,x,y,z) #define face3(i,j,k) fprintf(fp,"%d %d %d#\n", i,j,k) #define pi (4*atan(1.0)) void main() { FILE *fp; char ch, str[50]; float s, r, alpha, tau; int i; tau = (sqrt(5.0)+1)/2; // tau = 2*cos(pi/5); s = sqrt(3-tau)/2; // s = sin(pi/5), pi/5 = 36 degrees r = tau - 0.5; printf("Ten of the twelve vertices of the icosahedron\n"); printf("lie on two horizontal pentagons, either of which\n"); printf("fits in a circle with radius 1.\n"); printf("These two pentagons lie a distance 1 apart.\n"); printf("The edges have length %f.\n", 2*s); printf("The icosahedron fits in a sphere with radius %f.\n",r); 58 fp = fopen("Icosa.dat", "w"); fprintf(fp, "%f %f %f\n", 0., 0., 0.); point(1, 0., 0., r); // North pole for (i=0; i<5; i++) { alpha = -pi/5+i*pi/2.5; // In degree: -36+i*72 point(2+i, cos(alpha), sin(alpha), 0.5); } for (i=0; i<5; i++) { alpha = i*pi/2.5; point(7+i, cos(alpha), sin(alpha), -0.5); } point(12, 0., 0., -r); // South pole fprintf(fp, "Faces:\n"); for (i=0; i<5; i++) face3(1, 2+i, (i<4 ? 3+i : 2)); for (i=0; i<5; i++) face3(2+i, 7+i, (i<4 ? 3+i : 2)); for (i=0; i<5; i++) face3(7+i, 12, (i<4 ? 8+i : 7)); fclose(fp); } 20. Sphere approximation with 80 triangles // Sph80.cpp // Polyhedron with 80 triangular faces to approximate a sphere. // It is based on an icosahedron, which is a regular polyhedron // with 20 equilateral triangules as bounding faces. Instead of // each of these triangles, four smaller triangles are used, // which gives 20 x 4 = 80 0f these small triangles (no eqilateral). 59 // For each equilateral triangle of the icosahedron the midpoints // of its three sides are projected onto we obtain the four smaller // triangles by connecting neighboring points. #include <stdio.h> #include <math.h> #include <process.h> #define pi (4*atan(1.0)) void point(int i, float x, float y, float z); void subdivide(int A, int B, int C); void storeface(int i, int j, int k); void midpnt(int B, int C, int *pP, float x, float y, float z); int npoints; typedef struct Point3Struct {float x, y, z; } Point3; Point3 pnt[43]; int nface = 0; struct {int i, j, k; } fc[80]; int nedge = 0; struct { int i, j, P; } edges[120]; FILE *fp; void main() { float r, alpha, tau; int i, l; tau = (sqrt(5.0)+1)/2; // tau = 2*cos(pi/5); r = tau - 0.5; fp = fopen("Sph80.dat", "w"); fprintf(fp, "%f %f %f\n", 0., 0., 0.); point(1, 0., 0., 1.0); // North pole for (i=0; i<5; i++) { alpha = -pi/5+i*pi/2.5; // In degree: -36+i*72 point(2+i, cos(alpha)/r, sin(alpha)/r, 0.5/r); } for (i=0; i<5; i++) { alpha = i*pi/2.5; point(7+i, cos(alpha)/r, sin(alpha)/r, -0.5/r); 60 } point(12, 0., 0., -1.0); // South pole npoints = 12; for (i=0; i<5; i++) subdivide(1, 2+i, (i<4? 3+i : 2)); for (i=0; i<5; i++) subdivide(2+i, 7+i, (i<4? 3+i : 2)); for (i=0; i<5; i++) subdivide(7+i, (i<4? 8+i : 7), (i<4 ? 3+i : 2)); for (i=0; i<5; i++) subdivide(7+i, 12, (i<4 ? 8+i : 7)); fprintf(fp, "Faces:\n"); for (l=0; l<80; l++) fprintf(fp, "%d %d %d#\n", fc[l].i, fc[l].j, fc[l].k); fclose(fp); } void midpnt(int B, int C, int *pP, float x, float y, float z) // Point (x, y, z) is midpoint of BC. If it is a new vertex, store it and // write it to the object file, using a new vertex number. If not, find // its vertex number. The vertex number is to be assigned to *pP anyway. { int tmp, e; if (C < B) { tmp = B; B = C; C = tmp; } // B, C in increasing order, for the sake of uniqueness for (e=0; e<nedge; e++) if(edges[e].i == B && edges[e].j == C) break; if (e == nedge) // Not found, so we have a new vertex { edges[e].i = B; edges[e].j = C; edges[e].P = *pP = ++npoints; nedge++; point(*pP, x, y, z); } else *pP = edges[e].P; // Edge BC has been dealt with before, so // the vertex is not new } void point(int i, float x, float y, float z) { fprintf(fp, "%d %f %f %f\n", i, x, y, z); pnt[i].x = x; pnt[i].y = y; pnt[i].z = z; } void subdivide(int A, int B, int C) 61 // Divide triangle ABC into four smaller triangles { float xP, yP, zP, xQ, yQ, zQ, xR, yR, zR; int P, Q, R; static float d = -1; // d is equal to -1 only before the first call of this function; // after this, d will have its correct value (between 0 and 1), // namely the distance between any midpoint and the center of the // sphere. We project all midpoints onto the sphere (with radius 1) // by dividing their coordinates by d. xP = (pnt[B].x + pnt[C].x)/2; yP = (pnt[B].y + pnt[C].y)/2; zP = (pnt[B].z + pnt[C].z)/2; xQ = (pnt[C].x + pnt[A].x)/2; yQ = (pnt[C].y + pnt[A].y)/2; zQ = (pnt[C].z + pnt[A].z)/2; xR = (pnt[B].x + pnt[A].x)/2; yR = (pnt[B].y + pnt[A].y)/2; zR = (pnt[B].z + pnt[A].z)/2; if (d < 0) d = sqrt(xP*xP+yP*yP+zP*zP); // 0 < d < 1 xP /= d; yP /= d; zP /= d; xQ /= d; yQ /= d; zQ /= d; xR /= d; yR /= d; zR /= d; midpnt(B,C,&P,xP,yP,zP); midpnt(C,A,&Q,xQ,yQ,zQ); midpnt(A,B,&R,xR,yR,zR); storeface(A,R,Q); storeface(R,B,P); storeface(Q,P,C); storeface(Q,R,P); } void storeface(int i, int j, int k) { fc[nface].i = i; fc[nface].j = j; fc[nface].k = k; nface++; } 62 21. Three-dimensional rotations // Genrota.cpp #include <stdio.h> #include <math.h> #include <process.h> #define pi (4*atan(1.0)) #define point(i,P) fprintf(fp2,"%d %f %f %f\n",i,P.x,P.y,P.z) #define face3(i,j,k) fprintf(fp2,"%d %d %d#\n", i,j,k) typedef struct Point3Struct {double x, y, z; } Point3; void initrotate(Point3 A, Point3 V, double alpha); void rotate(Point3 A, Point3 *P1); double r11, r12, r13, r21, r22, r23, r31, r32, r33, r41, r42, r43; void main() { FILE *fp1, *fp2; Point3 A, P1, V; double alphadeg, alpha; int nr, ch, n=0; /* char str[30]; printf("Input file: "); scanf("%s", str); fp1 = fopen(str,"r"); printf("Output file: "); scanf("%s", str); fp2 = fopen(str,"w"); */ fp1 = fopen("gen.in","r"); fp2 = fopen("gen.dat","w"); if (fp1 == NULL || fp2 == NULL) { printf("File program"); exit(1); } printf("Enter xA, yA, zA: "); scanf("%lf %lf %lf", &(A.x), &(A.y), &(A.z)); printf("ENter V.x, V.y, V.z: "); 63 scanf("%lf %lf %lf", &(V.x), &(V.y), &(V.z)); printf("Enter alpha (in degrees): "); scanf("%lf", &alphadeg); alpha = alphadeg * pi/180; // alpha in radians initrotate(A, V, alpha); fscanf(fp1, "%lf %lf %lf",&(A.x), &(A.y), &(A.z)); fprintf(fp2,"%lf %lf %lf\n", A.x, A.y, A.z); while (fscanf(fp1, "%d %lf %lf %lf",&nr, &(A.x), &(A.y), &(A.z)) == 4) { rotate(A,&P1); n++; point(nr, P1); } while (ch = getc(fp1), ch != EOF) putc(ch,fp2); fclose(fp1); fclose(fp2); printf("Ready! %d points rotated\n", n); } void initrotate(Point3 A, Point3 V, double alpha) { double rho, theta, cal, sal, cph, sph, cth, sth, cph2, sph2, cth2, sth2, call; cal = cos(alpha); sal = sin(alpha); call = 1.0-cal; rho = sqrt(V.x*V.x+V.y*V.y+V.z*V.z); if (fabs(rho) < 1e-12) { theta = 0.0; cph = 1.0; sph = 0.0; } else { if (fabs(V.x) < 1e-12) theta = (V.y >= 1e-5? 0.5*pi : 1.5*pi); else { theta = atan(V.y/V.x); if (V.x < 0) theta += pi; cph = V.z/rho; sph = sqrt(1.0-cph*cph); // cph = cos(phi); sph = sin(phi); } cth = cos(theta); cph2 = cph*cph; cth2 = cth*cth; sth = sin(theta); sph2 = 1.0-cph2; sth2 = 1.0-cth2; r11 = (cal*cph2 + sph2)*cth2+cal*sth2; r12 = sal*cph+call*sph2*cth*sth; r13 = sph*(cph*cth*call-sal*sth); r21 = sph2*cth*sth*call-sal*cph; r22 = sth2*(cal*cph2+sph2)+cal*cth2; r23 = sph*(cph*sth*call+sal*cth); r31 = sph*(cph*cth*call+sal*sth); r32 = sph*(cph*sth*call-sal*cth); r33 = cal*sph2+cph2; 64 } r41 = A.x-A.x*r11-A.y*r21-A.z*r31; r42 = A.y-A.x*r12-A.y*r22-A.z*r32; r43 = A.z-A.x*r13-A.y*r23-A.z*r33; printf("%f %f %f\n",r11, r12, r13); printf("%f %f %f\n",r21, r22, r23); printf("%f %f %f\n",r31, r32, r33); printf("%f %f %f\n",r41, r42, r43); } void rotate(Point3 A, Point3 *P1) { P1->x = A.x*r11+A.y*r21+A.z*r31+r41; P1->y = A.x*r12+A.y*r22+A.z*r32+r42; P1->z = A.x*r13+A.y*r23+A.z*r33+r43; } // gen.in // gen.dat 0.0 0.0 0.0 0.000000 0.000000 0.000000 1 1.0 0.0 0.0 1 1.780631 0.982662 0.000000 2 1.0 2.0 0.0 2 -0.151221 1.500301 0.000000 3 0.0 2.0 0.0 3 -0.410040 0.534375 0.000000 4 0.0 0.0 0.0 4 1.521812 0.016737 0.000000 5 1.0 0.0 3.0 5 1.780631 0.982662 3.000000 6 1.0 2.0 3.0 6 -0.151221 1.500301 3.000000 7 0.0 2.0 3.0 7 -0.410040 0.534375 3.000000 8 0.0 0.0 3.0 8 1.521812 0.016737 3.000000 9 0.0 0.0 0.0 9 1.521812 0.016737 0.000000 10 4.0 0.0 0.0 10 2.557088 3.880440 0.000000 11 0.0 4.0 0.0 11 -2.341892 1.052013 0.000000 12 0.0 0.0 4.0 12 1.521812 0.016737 4.000000 Faces: Faces: 1 2 6 5# 1 2 6 5# 2 3 7 6# 2 3 7 6# 3 4 8 7# 3 4 8 7# 4 1 5 8# 4 1 5 8# 5 6 7 8# 5 6 7 8# 1 4 3 2# 1 4 3 2# 9 10# 9 10# 9 11# 9 11# 9 12# 9 12# 65 22. B-spline space curves // Curve3.cpp // B-spline curve fitting in 3D. The program reads an input file // and writes an output file. #include <stdio.h> #include <math.h> #include <process.h> #define Horner(t, a3, a2, a1, a0) ((((a3)*(t)+(a2))*(t)+(a1))*(t)+(a0)) typedef struct Point3Struct {float x, y, z; } Point3; typedef float Coef; typedef int Index; void main() { char infil[30], outfil[30]; Index i, j; int m=0, N, k, first = 0; float t; Coef a[4], b[4], c[4]; Point3 A, B, C, D, dum, *P, Q; FILE *fpin, *fpout; // printf("Input file: "); scanf("%s", infil); // printf("Output file: "); scanf("%s", outfil); printf("Enter N, the number of intervals between two successive given points: "); scanf("%d", &N); fpin = fopen("cable.dat","r"); if (fpin == NULL) { printf("File not available"); exit(1); } while (fscanf(fpin,"%*d %f %f %f", &(dum.x), &(dum.y), &(dum.z)) >0) m++; fclose(fpin); fpin = fopen("cable.dat", "r"); P = new Point3[m+1]; 66 for (i = 1; i <= m; i++) fscanf(fpin, "%*d %f %f %f", &((P+i)->x), &((P+i)->y), &((P+i)->z)); fclose(fpin); fpout = fopen("cable.out", "w"); for (i = 2; i < m-1; i++) { A = P[i-1]; B = P[i]; C = P[i+1]; a[3] = (-A.x+3*(B.x-C.x)+D.x)/6.0; a[2] = (A.x-2*B.x+C.x)/2.0; a[1] = (C.x-A.x)/2.0; a[0] = (A.x+4*B.x+C.x)/6.0; D = P[i+2]; b[3] = (-A.y+3*(B.y-C.y)+D.y)/6.0; b[2] = (A.y-2*B.y+C.y)/2.0; b[1] = (C.y-A.y)/2.0; b[0] = (A.y+4*B.y+C.y)/6.0; c[3] = (-A.z+3*(B.z-C.z)+D.z)/6.0; c[2] = (A.z-2*B.z+C.z)/2.0; c[1] = (C.z-A.z)/2.0; c[0] = (A.z+4*B.z+C.z)/6.0; for(j = first; j <= N; j++) { t = float(j)/float(N); Q.x = Horner(t, a[3], a[2], a[1], a[0]); Q.y = Horner(t, b[3], b[2], b[1], b[0]); Q.z = Horner(t, c[3], c[2], c[1], c[0]); fprintf(fpout, "%d %f %f %f\n", (i-2)*N+j+1, Q.x, Q.y, Q.z); } first = 1; } fprintf(fpout,"Faces:\n"); k = (m-3)*N+1; for (j = 1; j < k; j++) fprintf(fpout, "%d %d#\n", j, j+1); fclose(fpout); } // cable.dat 1 0 -100 12 2 0 -70 10 3 -15 -30 8 4 15 0 15 5 0 20 22 Faces: 1 2# 2 3# 3 4# 4 5# 5 6# 67 6 -15 30 0 7 0 20 -22 6 7# 7 8# 8 15 0 -15 9 -15 -30 -8 10 0 -70 -50 11 0 0 -100 12 0 70 -50 13 15 30 -8 14 -15 0 -15 15 0 -20 -22 16 15 -30 0 8 9# 9 10# 10 11# 11 12# 12 13# 13 14# 14 15# 15 16# 16 17# 17 0 -20 22 18 -15 0 15 19 15 30 8 20 0 70 10 21 0 100 12 17 18# 18 19# 19 20# 20 21# 23. Cables /* CABLE: This program reads a file that represents a space curve, and it writes a file that represents a cable. The circular cable section will be a regular polygon with n vertices; it approximates a circle with radius R. Both n and R are read from the keyboard. The program is to be linked together with the module TRAFO, in which the functions ‘initrotate' and 'rotate' are defined. */ #include <stdio.h> #include <math.h> 68 #include <process.h> #include <stdlib.h> void initrotate (double al, double a2, double a3, double vl, double v2, double v3, double alpha); void rotate (double x, double y, double z, double *pxl, double *pyl, double *pzl); int zero (double x) ; double *getdouble(int n); double *enlarge (double *px, int N) ; void ermes (char *s); void main() { char infil[30], outfil[30]; int i, n, j, m, jn, jn0, k, tablesize; double R, *x, *y, *z, xC0, yC0, zC0, xC1, yC1, zC1, xC2, yC2, zC2, *xC, *yC, *zC, a, b, c, d, rx, ry, rz, pi, theta, Len, xA, yA, zA, xB, yB, zB, dx, dy, dz, d0, c1, c2, c0, xM, yM, zM, e1, e2, e0, denom, lambda, mu, xP, yP, zP, xAP, yAP, zAP, xBP, yBP, zBP, v1, v2, v3, cosphi, phi; FILE *fpin, *fpout; printf ("Input file: "); scanf("%s", infil); fpin = fopen (infil, "r"); if (fpin == NULL) ermes ("File not available"); if (fscanf(fpin, "%*d %lf %lf %lf", &xC0, &yC0, &zC0) != 3 || fscanf(fpin, "%*d %lf %lf %lf", &xC1, &yC1, &zC1) != 3 || fscanf(fpin, "%*d %lf %lf %lf", &xC2, &yC2, &zC2) != 3) ermes ("Input file incorrect") ; printf ("Output file: "); scanf("%s", outfil); fpout = fopen(outfil, "w") ; printf ("How many points on each circle? "); scanf("%d", &n); printf ("Radius: "); scanf("%lf", &R); a=xC2-xC0; b=yC2-yC0; c=zC2-zC0; d=a*xC1+b*yC1+c*zC1; /* First circle has center (xCI, yCl, zCI), radius R, and it lies in plane ax+by+cz = d*/ x = getdouble(n); y = getdouble(n); z = getdouble(n); if (zero(a) && zero(b)) { rx=0; ry=c; rz=-b;} 69 else {rx=b; ry=-a; rz=0;} Len=sqrt(rx*rx+ry*ry+rz*rz); rx/=Len; ry/=Len; rz/=Len; /* (rx, ry, rz) is a unit vector perpendicular to (a, b, c) */ x[0]=xC1+rx*R; y[0]=yC1+ry*R; z[0]=zC1+rz*R; pi=4.0*atan(1.0); theta=2*pi/n; /* Computation of n points on the first circle: */ initrotate(xC1, yC1, zC1, a, b, c, theta); for (i=1; i<n; i++) rotate(x[i-1], y[i-1], z[i-1], x+i, y+i, z+i); /* Count number of circles (number of points minus I read from input file): */ m = 2; while (fscanf(fpin, "%*d %lf %lf %lf", &xC0, &yC0, &zC0) == 3) tablesize = m*n; x = enlarge(x, tablesize); y = enlarge(y, tablesize); z = enlarge(z, tablesize); fclose(fpin); fpin = fopen(infil, "r") ; /* Rewind */ fscanf(fpin, "%*d %lf %lf %lf", &xC0, &yC0, &zC0); /* Skip */ xC = getdouble(m); m++; yC = getdouble(m); zC = getdouble(m); for (j=0; j<m; j++) fscanf(fpin, "%*d %lf %lf %lf", xC+j, yC+j, zC+j); /* (xC [0], yC[0], zC[0]) is now the center of the given circle, lying in plane ax+by+cz =d, and with radius R. The n relevant points on this circle have already been computed; their coordinates are x[0], y[0], z[0], ..., x[n-l], y[n-l], z[n-l]. The other m-l circles will be derived from this first one by means of rotations. fclose(fpin); for (j=1; j<m; j++) { jn=j*n; jn0=jn-n; xA=xC[j-1] ; yA=yC[j-1] ; zA=zC[j-1] ; xB=xC[j]; yB=yC[j]; zB=zC[j]; dx=xB-xA; dy=yB-yA; dz=zB-zA; c1=a*a+b*b+c*c; c2=a*dx+b*dy+c*dz; c0=d-a*xA-b*yA-c*zA; xM=0.5*(xA+xB); yM=0.5*(yA+yB); zM=0.5*(zA+zB); 70 */ d0=dx*xM+dy*yM+dz*zM; e1=dx*a+dy*b+dz*c; e2=dx*dx+dy*dy+dz*dz; e0=d0-dx*xA-dy*yA-dz*zA; denom=c1*e2-c2*e1; if (fabs(denom) < 1e-12) { /* Direction does not change. Instead of using a point P infinitely far away, we perform a simple translation: for (i=0; i<n; i++) { */ x[jn+i] = x[jn0+i] + dx; y[jn+i] = y[jn0+i] + dy; z[jn+i] = z[jn0+i] + dz; } } else /* Direction changes. The polygon will be rotated through the angle phi about vector v passing through point P: */ { lambda=(c0*e2-c2*e0)/denom; mu= (c1*e0-c0*e1)/denom; xP=xA+lambda *a+mu*dx; yP=yA+lambda *b+mu*dy; zP=zA+lambda*c+mu*dz ; /* Point P (of intersection of three planes) is center of rotation */ xAP=xA-xP; yAP=yA-yP; zAP=zA-zP; xBP=xB-xP; yBP=yB-yP; zBP=zB-zP; v1=yAP*zBP-yBP*zAP; v2=xBP*zAP-xAP*zBP; v3=xAP*yBP-xBP*yAP; /* (vl, v2, v3) is direction of axis of rotation */ cosphi=(xAP*xBP+yAP*yBP+zAP*zBP)/ sqrt ( (xAP*xAP+yAP*yAP+zAP*zAP) * (xBP*xBP+yBP*yBP+zBP*zBP) ); phi = (cosphi == 0 ? 0.5*pi : 71 atan(sqrt(1.0-cosphi*cosphi)/cosphi) ) ; /* phi is the angle of rotation */ initrotate(xP, yP, zP, v1, v2, v3, phi); for (i=0; i<n; i++) rotate(x[jn0+i], y[jn0+i], z[jn0+i], x+jn+i, y+jn+i, z+jn+i); initrotate(0.0, 0.0, 0.0, v1, v2, v3, phi); rotate(a, b, c, &a, &b, &c) ; } d=a*xC[j]+b*yC[j]+c*zC[j] ; } if (fpout == NULL) ermes ("Problem with output file"); for (k=0; k<m*n; k++) fprintf (fpout, "%d %f %f %f\n", k+1, x[k], y[k], z[k]); fprintf (fpout, "Faces:\n"); for (k=n-1; k>=0; k--) fprintf(fpout, " %d", k+1); fprintf ( fpout, "#\n" ) ; for (k=(m-1)*n; k<m*n; k++) fprintf(fpout, " %d", k+1); fprintf( fpout, "#\n\n" ) ; for (j=1; j<m; j++) { jn=j*n+1; jn0=jn-n; for (i=0; i<n-1; i++) { fprintf(fpout, " %d %d %d#\n", jn+i+1, -(jn0+i), jn0+i+1) ; fprintf(fpout, " %d %d %d#\n", jn0+i, -(jn+i+1), jn+i); } fprintf(fpout, " %d %d %d#\n\n", jn, -(jn0+n-1), jn0); fprintf(fpout, " %d %d %d#\n\n", jn0+n-1, -jn, jn+n-1); } fclose (fpout); } int zero (double x) { return fabs(x) < 1e-5;} double *getdouble(int n) { double *p; p = new double[n]; if (p == NULL) ermes ("Not enough memory"); 72 return p; } double *enlarge (double *px, int N) { px = (double *)realloc(px, N*sizeof(double)); if (px == NULL) ermes("Not enough memory") ; return px; } void ermes (char *s) { printf(s); exit (1); } /* CABLE: This program reads a file that represents a space curve, and it writes a file that represents a cable. The circular cable section will be a regular polygon with n vertices; it approximates a circle with radius R. Both n and R are read from the keyboard. The program is to be linked together with the module TRAFO, in which the functions ‘initrotate' and 'rotate' are defined. */ #include <stdio.h> #include <math.h> #include <process.h> #include <stdlib.h> typedef struct Point3Struct {float x, y, z; } Point3; typedef Point3 Vector3; typedef int Index; #define Length2(P) ((P.x*P.x)+(P.y*P.y)+(P.z*P.z)) #define Length(P) (sqrt(Length2(P))) #define pi (4.0*atan(1.0)) Point3 V3Sub(Vector3 p, Vector3 q); Point3 V3Add(Vector3 p, Vector3 q); float V3Dot(Vector3 p, Vector3 q); Vector3 V3Cross(Vector3 a, Vector3 b); Vector3 V3Normalize(Vector3 v); int zero(float x) ; Point3 *enlarge(Point3 *px, int N) ; void ermes(char *s); void initrotate(Point3 A, Point3 V, float alpha); 73 void rotate(Point3 A, Point3 *P1); float r11, r12, r13, r21, r22, r23, r31, r32, r33, r41, r42, r43; void main() { char infil[30], outfil[30]; Index i, j, k; int n, m, mn, jn, jn0, tablesize; Point3 C0, C1, C2, *C, A, B, M, P, AP, BP, r, *T, d; Vector3 Ce, V; float radius, d1,theta, d0, c1, c2, c0, e1, e2, e0, denom, lambda, mu, cosphi, phi; FILE *fpin, *fpout; // printf ("Input file: "); scanf("%s", infil); fpin = fopen("cable.in", "r"); if (fpin == NULL) ermes ("File not available"); if (fscanf(fpin, "%*d %f %f %f", &(C0.x), &(C0.y), &(C0.z)) != 3 || fscanf(fpin, "%*d %f %f %f", &(C1.x), &(C1.y), &(C1.z)) != 3 || fscanf(fpin, "%*d %f %f %f", &(C2.x), &(C2.y), &(C2.z)) != 3) ermes ("Input file incorrect") ; // printf ("Output file: "); scanf("%s", outfil); fpout = fopen("cable.out", "w") ; printf ("How many points on each circle? "); scanf("%d", &n); printf ("Radius: "); scanf("%f", &radius); Ce = V3Sub(C2, C0); d1 = V3Dot(Ce, C1); /* First circle has center (C1.x, C1.y, C1.z), radius radius, and it lies in plane ax+by+cz = d*/ T = new Point3[n]; // Point3 T[n] if (zero(Ce.x) && zero(Ce.y)) { r.x = 0; r.y = Ce.z; r.z = -Ce.y;} else { r.x = Ce.y; r.y = -Ce.x; r.z = 0;} r = V3Normalize(r); /* (rx, ry, rz) is a unit vector perpendicular to (a, b, c) T[0].x = C1.x+r.x*radius; T[0].y = C1.y+r.y*radius; T[0].z = C1.z+r.z*radius; theta = 2*pi/n; /* Computation of n points on the first circle: */ initrotate(C1, Ce, theta); for (i = 1; i < n; i++) rotate(T[i-1],&(T[i])); /* Count number of circles 74 */ (number of points minus I read from input file): */ m = 2; while (fscanf(fpin, "%*d %f %f %f", &(C0.x), &(C0.y), &(C0.z)) == 3) m++; tablesize = m*n; T = enlarge(T, tablesize); fclose(fpin); fpin = fopen("cable.in", "r") ; /* Rewind */ fscanf(fpin, "%*d %f %f %f", &(C0.x), &(C0.y), &(C0.z)); /* Skip */ C = new Point3[m]; // Point3 C[m] for (j = 0; j < m; j++) fscanf(fpin, "%*d %f %f %f", &(C[j].x), &(C[j].y), &(C[j].z)); /* (xC [0], yC[0], zC[0]) is now the center of the given circle, lying in plane ax+by+cz =d, and with radius R. The n relevant points on this circle have already been computed; their coordinates are x[0], y[0], z[0], ..., x[n-l], y[n-l], z[n-l]. The other m-l circles will be derived from this first one by means of rotations. */ fclose(fpin); for (j = 1; j < m; j++) { jn=j*n; jn0=jn-n; A = C[j-1]; B = C[j]; d = V3Sub(B, A); c1 = Length2(Ce); c2 = V3Dot(Ce, d); c0 = d1-V3Dot(Ce, A); M.x=0.5*(A.x+B.x); M.y=0.5*(A.y+B.y); M.z=0.5*(A.z+B.z); d0= V3Dot(d, M); e1 = V3Dot(d, Ce); e2 = Length2(d); e0 = d0-V3Dot(d, A); denom = c1*e2-c2*e1; if (fabs(denom) < 1e-12) { /* Direction does not change. Instead of using a point P infinitely far away, we perform a simple translation: */ for (i=0; i<n; i++) T[jn+i] = V3Add(T[jn0+i], d); } else /* Direction changes. The polygon will be rotated through the angle phi about vector v passing through point P: */ { lambda = (c0*e2-c2*e0)/denom; 75 mu = (c1*e0-c0*e1)/denom; P.x = A.x + lambda*Ce.x + mu*d.x; P.y = A.y + lambda*Ce.y + mu*d.y; P.z = A.z + lambda*Ce.z + mu*d.z ; /* Point P (of intersection of three planes) is center of rotation */ AP = V3Sub(A, P); BP = V3Sub(B, P); V = V3Cross(AP, BP); /* (V.x, V.y, V.z) is direction of axis of rotation */ cosphi = V3Dot(AP,BP)/(Length(AP) * Length(BP)); phi = (cosphi == 0 ? 0.5*pi : atan(sqrt(1.0-cosphi*cosphi)/cosphi) ) ; /* phi is the angle of rotation */ initrotate(P, V, phi); for (i = 0; i < n; i++) P.x = P.y = P.z = 0.0; initrotate(P, V, phi); rotate(Ce, &(Ce)) ; rotate(T[jn0+i], &(T[jn+i])); } d1 = V3Dot(Ce, C[j]); } if (fpout == NULL) ermes ("Problem with output file"); mn = m*n; for (k = 0; k < mn; k++) fprintf (fpout, "%d %f %f %f\n", k+1, T[k].x, T[k].y, T[k].z); fprintf (fpout, "Faces:\n"); for (k = n-1; k >= 0; k--) fprintf(fpout, " %d", k+1); fprintf ( fpout, "#\n" ) ; for (k = (m-1)*n; k < mn; k++) fprintf(fpout, " %d", k+1); fprintf( fpout, "#\n\n" ) ; for (j = 1; j < m; j++) { jn = j*n+1; jn0 = jn-n; for (i = 0; i < n-1; i++) { fprintf(fpout, " %d %d %d#\n", jn+i+1, -(jn0+i), jn0+i+1) ; fprintf(fpout, " %d %d %d#\n", jn0+i, -(jn+i+1), jn+i); } fprintf(fpout, " %d %d %d#\n\n", jn, -(jn0+n-1), jn0); fprintf(fpout, " %d %d %d#\n\n", jn0+n-1, -jn, jn+n-1); } fclose (fpout); 76 } int zero (float x) { return fabs(x) < 1e-5;} Point3 *enlarge (Point3 *px, int N) { px = (Point3 *)realloc(px, N*sizeof(Point3)); if (px == NULL) ermes("Not enough memory") ; return px; } void ermes (char *s) { printf(s); exit (1); } Vector3 V3Sub(Vector3 p, Vector3 q) { Vector3 r; r.x = p.x - q.x; r.y = p.y - q.y; r.z = p.z - q.z; } Vector3 V3Add(Vector3 p, Vector3 q) { Vector3 r; r.x = p.x + q.x; r.y = p.y + q.y; r.z = p.z + q.z; } return(r); return(r); float V3Dot(Vector3 p, Vector3 q) { return((p.x*q.x)+(p.y*q.y)+(p.z*q.z)); } Vector3 V3Normalize(Vector3 v) { float len = Length(v); if (len != 0.0) {v.x /= len; return(v); v.y /= len; v.z /= len;}; } /*回返 c = a × b*/ Vector3 V3Cross(Vector3 a, Vector3 b) { 77 Vector3 c; c.x = (a.y * b.z) - (a.z * b.y); c.y = (a.z * b.x) - (a.x * b.z); c.z = (a.x * b.y) - (a.y * b.x); return (c); } void initrotate(Point3 A, Point3 V, float alpha) { float rho, theta, cal, sal, cph, sph, cth, sth, cph2, sph2, cth2, sth2, call; cal = cos(alpha); sal = sin(alpha); call = 1.0-cal; rho = Length(V); if (rho == 0.0) { theta = 0.0; cph = 1.0; sph = 0.0; } else { if (V.x == 0.0) theta = (V.y >= 0.0? 0.5*pi : 1.5*pi); else { theta = atan(V.y/V.x); if (V.x<0) theta += pi; } cph = V.z/rho; sph = sqrt(1.0-cph*cph); } cth = cos(theta); sth = sin(theta); cph2 = cph*cph; sph2 = 1.0-cph2; cth2 = cth*cth; sth2 = 1.0-cth2; r11 = (cal*cph2 + sph2)*cth2+cal*sth2; r12 = sal*cph+call*sph2*cth*sth; r13 = sph*(cph*cth*call-sal*sth); r21 = sph2*cth*sth*call-sal*cph; r22 = sth2*(cal*cph2+sph2)+cal*cth2; r23 = sph*(cph*sth*call+sal*cth); r31 = sph*(cph*cth*call+sal*sth); r32 = sph*(cph*sth*call-sal*cth); r33 = cal*sph2+cph2; r41 = A.x-A.x*r11-A.y*r21-A.z*r31; r42 = A.y-A.x*r12-A.y*r22-A.z*r32; r43 = A.z-A.x*r13-A.y*r23-A.z*r33; 78 // cph = cos(phi); sph = sin(phi); } void rotate(Point3 A, Point3 *P1) { P1->x = A.x*r11+A.y*r21+A.z*r31+r41; P1->y = A.x*r12+A.y*r22+A.z*r32+r42; P1->z = A.x*r13+A.y*r23+A.z*r33+r43; } 24. B-spline surfaces /* BSPLSURF: B-spline surface. This program expects a D3D object, file in which a grid of points is defined. The program asks the user to enter m and n: there must be exactly mn points in the file, numbered 1, 2, ..., mn. It is assumed that these points are arranged in a grid as follows: 1 2 ... m m+1 m+2 ... 2m 2m+1 2m+2 ... 3m . . . (n-l)m+1 . (n-1)+2 ... mn Neither m nor n must be less than 4. The position of the points in 3D space is free: no two points need have the same x-, y-, or z-coordinates. If the object file contains a section beginning with "Faces:", then this section is ignored. Two integers M and N are to be entered; they denote 79 numbers of intervals used in the surface-fitting process. There will be M intervals between the points 1 and 2, and N intervals between the points 1 and m+1, and so on. The B-spline curved surface that approximates the given points is written to an output file in D3D format. */ #include <stdio.h> #include <process.h> void main() { FILE *fp1, *fp2; char str[30] ; float *xx, *yy, *zz, x, y, z, v, u, u2, u3, a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33, b00, b01, b02, b03, b10, b11, b12, b13, b20, b21, b22, b23, b30, b31, b32, b33, c00, c01, c02, c03, c10, c11, c12, c13, c20, c21, c22, c23, c30, c31, c32, c33, x00, x01, x02, x03, x10, x11, x12, x13, x20, x21, x22, x23, x30, x31, x32, x33, y00, y01, y02, y03, y10, y11, y12, y13, y20, y21, y22, y23, y30, y31, y32, y33, z00, z01, z02, z03, z10, z11, z12, z13, z20, z21, z22, z23, z30, z31, z32, z33; int size, i,j,k, l, m, n, M, N, mn, I, J, A, B, C, ii, jj, nn, mm, P, h00, h01, h02, h03, h10, h11, h12, h13, h20, h21, h22, h23, h30, h31, h32, h33; do { printf ("Enter m and n (both at least 4): "); scanf ("%d %d", &m, &n); } while (m < 4 || n < 4); printf ("Input file: "); scanf("%s", str); fp1 = fopen (str, "r"); printf ("Output file: "); scanf("%s", str); fp2 = fopen (str, "w"); if (fp1 == NULL || fp2 == NULL) { printf ("File problem"); exit(1); } mn = m * n; xx = new float[mn+1]; yy = new float[mn+1]; zz =new float[mn+1]; for (l = 0; l < mn; l++) { if (fscanf(fp1, "%d %f %f %f", &k, &x, &y, &z) != 4) { printf ("Too few points in file"); exit (1); } 80 if (k < 1 || k > mn) { printf ("Point number %d incorrect", k); exit(1); } xx[k] = x; yy[k] = y; zz[k] = z; } fclose(fp1); printf ("Enter M and N: "); scanf("%d %d", &M, &N); /* Let us imagine that we have n rows of m points, which gives (n-1) * (m-1) rectangles. Among these, only (n-3) * (m-3) rectangles will be approximated. We consider each of these rectangles to be divided into M x N tiny rectangles, which we will call 'elements'. There will be A points in the output file for each horizontal grid line: */ A = (m-3) * M+1; printf (" i j\n"); for (i = 2; i <= n-2; i++) for (j = 2; j <= m-2; j++) { h11 = (i-1) * m+j; h10 = h11-1; h12 = h10+2; h13 = h10+3; h00 = h10-m; h01 = h00+1; h02 = h00+2; h03 = h00+3; h20 = h10+m; h21 = h20+1; h22 = h20+2; h23 = h20+3; h30 = h20+m; h31 = h30+1; h32 = h30+2; h33 = h30+3; printf("%3d %3d\n", i, j); /* Show that we are busy */ /* Here the 16 points are numbered as follows: P00 P01 P02 P03 P10 P11 P12 P13 P20 P21 P22 P23 P30 P31 P32 P33 The inner region, within P11, P12, P21, P22, will be approximated in this step (with the current values of i and j ). */ x00 = xx[h00]; y00 = yy[h00]; z00 = zz[h00]; x01 = xx[h01]; y01 = yy[h01]; z01 = zz[h01]; x02 = xx[h02]; y02 = yy[h02]; z02 = zz[h02]; x03 = xx[h03]; y03 = yy[h03]; z03 = zz[h03]; x10 = xx[h10]; y10 = yy[h10]; z10 = zz[h10]; x11 = xx[h11]; y11 = yy[h11]; z11 = zz[h11]; x12 = xx[h12]; y12 = yy[h12]; z12 = zz[h12]; x13 = xx[h13]; y13 = yy[h13]; z13 = zz[h13]; 81 x20 = xx[h20]; y20 = yy[h20]; z20 = zz[h20]; x21 = xx[h21]; y21 = yy[h21]; z21 = zz[h21]; x22 = xx[h22]; y22 = yy[h22]; z22 = zz[h22]; x23 = xx[h23]; y23 = yy[h23]; z23 = zz[h23]; x30 = xx[h30]; y30 = yy[h30]; z30 = zz[h30]; x31 = xx[h31]; y31 = yy[h31]; z31 = zz[h31]; x32 = xx[h32]; y32 = yy[h32]; z32 = zz[h32]; x33 = xx[h33]; y33 = yy[h33]; z33 = zz[h33]; a33 = (x00-x03-x30+x33+3*(-x01+x02-x10+x13+x20-x23+x31-x32) + 9*(x11-x12-x21+x22))/36; a32 = (-x00-x02+x30+x32 + 2*(x01-x31) + 3*(x10+x12-x20-x22) + 6*(-x11+x21))/12; a31 = (x00-x02-x30+x32 + 3*(-x10+x12+x20-x22))/12; a30 = (-x00-x02+x30+x32 + 3*(x10+x12-x20-x22) + 4*(-x01+x31) + 12*(x11-x21))/36; a23 = (-x00+x03-x20+x23 + 2*(x10-x13) + 3*(x01-x02+x21-x22) + 6*(x12-x11))/12; a22 = (x00+x02+x20+x22 + 2*(-x01-x10-x12-x21))/4 + x11; a21 = (x02-x00-x20+x22 + 2*(x10-x12))/4; a20 = (x00+x02+x20+x22 - 2*(x10+x12)+ 4*(x01+x21) - 8*x11)/12; a13 = (x00-x03-x20+x23 + 3*(x02-x01+x21-x22))/12; a12 = (-x00-x02+x20+x22 + 2*(x01-x21))/4; a11 = (x00-x02-x20+x22)/4; a10 = (-x00-x02+x20+x22 + 4*(-x01+x21))/12; a03 = (x03-x00-x20+x23 + 3*(x01-x02+x21-x22) + 4*(x13-x10) + 12*(x11-x12))/36; a02 = (x00+x02+x20+x22 - 2*(x01+x21) + 4*(x10+x12) - 8*x11)/12; a01 = (x02-x00-x20+x22 + 4*(x12-x10))/12; a00 = (x00+x02+x20+x22 + 4*(x01+x10+x12+x21)+ 16*x11)/36; b33 = (y00-y03-y30+y33 + 3*(-y01+y02-y10+y13+y20-y23+y31-y32) +9*(y11-y12-y21+y22))/36; b32 = (-y00-y02+y30+y32 + 2*(y01-y31)+ 3*(y10+y12-y20-y22) + 6*(-y11+y21))/12; b31 = (y00-y02-y30+y32 + 3*(-y10+y12+y20-y22))/12; b30 = (-y00-y02+y30+y32 + 3*(y10+y12-y20-y22) + 4*(-y01+y31) + 12*(y11-y21))/36; b23 = (-y00+y03-y20+y23 + 2*(y10-y13)+ 3*(y01-y02+y21-y22) + 6*(y12-y11))/12; b22 = (y00+y02+y20+y22 + 2*(-y01-y10-y12-y21))/4 + y11; b21 = (y02-y00-y20+y22 + 2*(y10-y12))/4; 82 b20 = (y00+y02+y20+y22 - 2*(y10+y12) + 4*(y01+y21) - 8*y11)/12; b13 = (y00-y03-y20+y23 + 3*(y02-y01+y21-y22))/12; b12 = (-y00-y02+y20+y22 + 2*(y01-y21))/4; b11 = (y00-y02-y20+y22)/4; b10 = (-y00-y02+y20+y22 + 4*(-y01+y21))/12; b03 = (y03-y00-y20+y23 + 3*(y01-y02+y21-y22) + 4*(y13-y10) + 12*(y11-y12))/36; b02 = (y00+y02+y20+y22 - 2*(y01+y21) + 4*(y10+y12) - 8*y11)/12; b01 = (y02-y00-y20+y22 + 4*(y12-y10))/12; b00 = (y00+y02+y20+y22 + 4*(y01+y10+y12+y21) + 16*y11)/36; c33 = (z00-z03-z30+z33 + 3*(-z01+z02-z10+z13+z20-z23+z31-z32) +9*(z11-z12-z21+z22))/36; c32 = (-z00-z02+z30+z32 + 2*(z01-z31) + 3*(z10+z12-z20-z22) + 6*(-z11+z21))/12; c31 = (z00-z02-z30+z32 + 3*(-z10+z12+z20-z22))/12; c30 = (-z00-z02+z30+z32 + 3*(z10+z12-z20-z22)+ 4*(-z01+z31) + 12*(z11-z21))/36; c23 = (-z00+z03-z20+z23 + 2*(z10-z13)+ 3*(z01-z02+z21-z22) + 6*(z12-z11))/12; c22 = (z00+z02+z20+z22 + 2*(-z01-z10-z12-z21))/4 + z11; c21 = (z02-z00-z20+z22 + 2*(z10-z12))/4; c20 = (z00+z02+z20+z22 - 2*(z10+z12) + 4*(z01+z21) - 8*z11)/12; c13 = (z00-z03-z20+z23 + 3*(z02-z01+z21-z22))/12; c12 = (-z00-z02+z20+z22 + 2*(z01-z21))/4; c11 = (z00-z02-z20+z22)/4; c10 = (-z00-z02+z20+z22 + 4*(-z01+z21))/12; c03 = (z03-z00-z20+z23 + 3*(z01-z02+z21-z22)+ 4*(z13-z10) + 12*(z11-z12))/36; c02 = (z00+z02+z20+z22 - 2*(z01+z21) + 4*(z10+z12) - 8*z11)/12; c01 = (z02-z00-z20+z22 + 4*(z12-z10))/12; c00 = (z00+z02+z20+z22 + 4*(z01+z10+z12+z21) + 16*z11)/36; /* In the output file we will be using point numbers k, which are to be computed from the controlled variables i, j, I, J. In each rectangle, determined by the pair (i, j) and to be divided into N x M elements, we use I, counting up to N, and J, counting up to M. To avoid duplicated points, I and J normally start at I, except for the lowest values (2) of i and j; then I and J start at 0, respectively. (Of all rectangles that are to be divided into elements, the one with i=j=2 is the leftmost rectangle at the top.) 83 If I=0 (which implies i=2), then k = (j-2)M+J+1, otherwise k = A+ (i-2)NA + (I-1)A + (j-2)M + J+ 1. This means that in either case we have: k = {(i-2)N + I}A + {(j-2)M +1}+J (A is the total number of mesh points on a horizontal row. ) */ B = (j-2)*M+1; for (I = (i == 2 ? 0 : 1);I <= N; I++) { u = float(I)/N; u2 = u*u; u3 = u2*u; C = ((i-2)*N+I)*A + B; for (J = (j == 2 ? 0: 1); J <= M; J++) { k = C + J; v = float(J)/M; x = a03*v+a02)*v+a01)*v+a00+u*(((a13*v+a12)*v+a11)*v+a10) +u2*(((a23*v+a22)*v+a21)*v+a20)+u3*(((a33*v+a32)*v+a31)*v+a30); y = ((b03*v+b02)*v+b01)*v+b00+u*(((b13*v+b12)*v+b11)*v+b10)+ u2*(((b23*v+b22)*v+b21)*v+b20)+u3*(((b33*v+b32)*v+b31)*v+b30); z = ((c03*v+c02)*v+c01)*v+c00+u*(((c13*v+c12)*v+c11)*v+c10)+ u2*(((c23*v+c22)*v+c21)*v+c20)+u3*(((c33*v+c32) *v+c31)*v+c30); fprintf(fp2, "%d %f %f %f\n", k, x, y, z); } } } fprintf ( fp2, "Faces: \n" ); nn = (n-3) * N; mm = A-1; /* mm = (m-3)*M */ for (ii = 0; ii < nn; ii++) for (jj = 0; jj < mm; jj++) { P = ii * A + jj + 1; /* We have to divide each (nearly rectangular) surface element into two triangles to ensure that the points that will be used as vertices of polygons really lie in the same plane. The minus sign in, for example, p -Q R will prevent D3D from drawing line segment PQ. Since either face of each triangle may be visible, we specify its vertices both counter-clockwise and clockwise: */ 84 fprintf(fp2, "%d %d %d#\n", P, -(P+A+1), P+1); fprintf(fp2, "%d %d %d#\n", P+A+1, -P, P+A); fprintf(fp2, "%d %d %d#\n", P, -(P+A+1), P+A); fprintf(fp2, "%d %d %d#\n", P+A+1, -P, P+1); } fclose(fp2); } /* BSPLSURF: B-spline surface. This program expects a D3D object, file in which a grid of points is defined. The program asks the user to enter m and n: there must be exactly mn points in the file, numbered 1, 2, ..., mn. It is assumed that these points are arranged in a grid as follows: 1 2 ... m m+1 m+2 ... 2m 2m+1 2m+2 ... 3m . . . . (n-l)m+1 (n-1)+2 ... mn Neither m nor n must be less than 4. The position of the points in 3D space is free: no two points need have the same x-, y-, or z-coordinates. If the object file contains a section beginning with "Faces:", then this section is ignored. Two integers M and N are to be entered; they denote numbers of intervals used in the surface-fitting process. There will be M intervals between the points 1 and 2, and N intervals between the points 1 and m+1, and so on. The B-spline curved surface that approximates the given points is written to an output file in D3D format. */ #include <stdio.h> #include <process.h> #define Horner(t, a3, a2, a1, a0) ((((a3)*(t)+(a2))*(t)+(a1))*(t)+(a0)) typedef struct Point3Struct {float x, y, z; } Point3; typedef float Coef; typedef int Index; void main() { FILE *fp1, *fp2; char str[30] ; Point3 *Q, T, P[4][4]; 85 float v, u, u2, u3, a[4][4], b[4][4], c[4][4]; Index i, j, k, r, c1, I, J, ii, jj; int do { m, n, M, N, mn, A, B, C, nn, mm, p, h[4][4]; printf ("Enter m and n (both at least 4): "); scanf ("%d %d", &m, &n); } while (m < 4 || n < 4); printf ("Input file: "); scanf("%s", str); fp1 = fopen (str, "r"); printf ("Output file: "); scanf("%s", str); fp2 = fopen (str, "w"); if (fp1 == NULL || fp2 == NULL) { printf ("File problem"); exit(1); } mn = m * n; Q = new Point3[mn+1]; // Point3 Q[mn+1] for (i = 0; i < mn; i++) { if (fscanf(fp1, "%d %f %f %f", &k, &(T.x), &(T.y), &(T.z)) != 4) { printf ("Too few points in file"); exit (1); } if (k < 1 || k > mn) { printf ("Point number %d incorrect", k); exit(1); Q[k].x = T.x; Q[k].y = T.y; Q[k].z = T.z; } } fclose(fp1); printf ("Enter M and N: "); scanf("%d %d", &M, &N); /* Let us imagine that we have n rows of m points, which gives (n-1) * (m-1) rectangles. Among these, only (n-3) * (m-3) rectangles will be approximated. We consider each of these rectangles to be divided into M x N tiny rectangles, which we will call 'elements'. There will be A points in the output file for each horizontal grid line: */ A = (m-3) * M+1; printf (" i j\n"); for (i = 2; i <= n-2; i++) for (j = 2; j <= m-2; j++) { h[1][1] = (i-1) * m+j; h[1][0] = h[1][1]-1; h[1][2] = h[1][0]+2; h[1][3] = h[1][0]+3; 86 h[0][0] = h[1][0]-m; h[0][1] = h[0][0]+1; h[0][2] = h[0][0]+2; h[0][3] = h[0][0]+3; h[2][0] = h[1][0]+m; h[2][1] = h[2][0]+1; h[2][2] = h[2][0]+2; h[2][3] = h[2][0]+3; h[3][0] = h[2][0]+m; h[3][1] = h[3][0]+1; h[3][2] = h[3][0]+2; h[3][3] = h[3][0]+3; printf("%3d %3d\n", i, j); /* Show that we are busy */ /* Here the 16 points are numbered as follows: P00 P01 P02 P03 P10 P11 P12 P13 P20 P21 P22 P23 P30 P31 P32 P33 The inner region, within P11, P12, P21, P22, will be approximated in this step (with the current values of i and j ). */ for (r = 0; r < 4; r++) for (c1 = 0; c1 < 4; c1++) P[r][c1] = Q[h[r][c1]]; a[3][3] = (P[0][0].x-P[0][3].x-P[3][0].x+P[3][3].x+ 3*(-P[0][1].x+P[0][2].x-P[1][0].x+P[1][3].x+P[2][0].x -P[2][3].x+P[3][1].x-P[3][2].x) + 9*(P[1][1].x-P[1][2].xP[2][1].x+P[2][2].x))/36; a[3][2] = (-P[0][0].x-P[0][2].x+P[3][0].x+P[3][2].x + 2*(P[0][1].x-P[3][1].x) + 3*(P[1][0].x+P[1][2].x-P[2][0].x-P[2][2].x) + 6*(-P[1][1].x+P[2][1].x))/12; a[3][1] = (P[0][0].x-P[0][2].x-P[3][0].x+P[3][2].x + 3*(-P[1][0].x+ P[1][2].x+P[2][0].x-P[2][2].x))/12; a[3][0] = (-P[0][0].x-P[0][2].x+P[3][0].x+P[3][2].x + 3*(P[1][0].x+ P[1][2].x-P[2][0].x-P[2][2].x) + 4*(-P[0][1].x+P[3][1].x) + 12*(P[1][1].x-P[2][1].x))/36; a[2][3] = (-P[0][0].x+P[0][3].x-P[2][0].x+P[2][3].x + 2*(P[1][0].x-P[1][3].x) + 3*(P[0][1].x-P[0][2].x+P[2][1].x-P[2][2].x) + 6*(P[1][2].x-P[1][1].x))/12; a[2][2] = (P[0][0].x+P[0][2].x+P[2][0].x+P[2][2].x + 2*(-P[0][1].x-P[1][0].x-P[1][2].x-P[2][1].x))/4 + P[1][1].x; a[2][1] = (P[0][2].x-P[0][0].x-P[2][0].x+P[2][2].x + 2*(P[1][0].x-P[1][2].x))/4; a[2][0] = (P[0][0].x+P[0][2].x+P[2][0].x+P[2][2].x - 2*(P[1][0].x+P[1][2].x)+ 4*(P[0][1].x+P[2][1].x) - 8*P[1][1].x)/12; a[1][3] = (P[0][0].x-P[0][3].x-P[2][0].x+P[2][3].x + 3*(P[0][2].x-P[0][1].x+P[2][1].x-P[2][2].x))/12; 87 a[1][2] = (-P[0][0].x-P[0][2].x+P[2][0].x+P[2][2].x + 2*(P[0][1].x-P[2][1].x))/4; a[1][1] = (P[0][0].x-P[0][2].x-P[2][0].x+P[2][2].x)/4; a[1][0] = (-P[0][0].x-P[0][2].x+P[2][0].x+P[2][2].x + 4*(-P[0][1].x+P[2][1].x))/12; a[0][3] = (P[0][3].x-P[0][0].x-P[2][0].x+P[2][3].x + 3*(P[0][1].x-P[0][2].x+P[2][1].x-P[2][2].x) + 4*(P[1][3].x-P[1][0].x) + 12*(P[1][1].x-P[1][2].x))/36; a[0][2] = (P[0][0].x+P[0][2].x+P[2][0].x+P[2][2].x 2*(P[0][1].x+P[2][1].x) + 4*(P[1][0].x+P[1][2].x) 8*P[1][1].x)/12; a[0][1] = (P[0][2].x-P[0][0].x-P[2][0].x+P[2][2].x + 4*(P[1][2].x-P[1][0].x))/12; a[0][0] = (P[0][0].x+P[0][2].x+P[2][0].x+P[2][2].x + 4*(P[0][1].x + P[1][0].x+P[1][2].x+P[2][1].x)+ 16*P[1][1].x)/36; b[3][3] = (P[0][0].y-P[0][3].y-P[3][0].y+P[3][3].y + 3*(-P[0][1].y+P[0][2].y-P[1][0].y+P[1][3].y+ P[2][0].y-P[2][3].y+P[3][1].y-P[3][2].y) + 9*(P[1][1].y-P[1][2].y-P[2][1].y+P[2][2].y))/36; b[3][2] = (-P[0][0].y-P[0][2].y+P[3][0].y+P[3][2].y + 2*(P[0][1].y-P[3][1].y) + 3*(P[1][0].y+P[1][2].y-P[2][0].y-P[2][2].y) + 6*(-P[1][1].y+P[2][1].y))/12; b[3][1] = (P[0][0].y-P[0][2].y-P[3][0].y+P[3][2].y + 3*(-P[1][0].y+P[1][2].y+P[2][0].y-P[2][2].y))/12; b[3][0] = (-P[0][0].y-P[0][2].y+P[3][0].y+P[3][2].y + 3*(P[1][0].y+P[1][2].y-P[2][0].y-P[2][2].y) + 4*(-P[0][1].y+P[3][1].y)+12*(P[1][1].y-P[2][1].y))/36; b[2][3] = (-P[0][0].y+P[0][3].y-P[2][0].y+P[2][3].y + 2*(P[1][0].y-P[1][3].y)+ 3*(P[0][1].y-P[0][2].y+P[2][1].y-P[2][2].y) + 6*(P[1][2].y-P[1][1].y))/12; b[2][2] = (P[0][0].y+P[0][2].y+P[2][0].y+P[2][2].y + 2*(-P[0][1].y-P[1][0].y-P[1][2].y-P[2][1].y))/4 + P[1][1].y; b[2][1] = (P[0][2].y-P[0][0].y-P[2][0].y+P[2][2].y + 2*(P[1][0].y-P[1][2].y))/4; b[2][0] = (P[0][0].y+P[0][2].y+P[2][0].y+P[2][2].y 2*(P[1][0].y+P[1][2].y) + 4*(P[0][1].y+P[2][1].y) 8*P[1][1].y)/12; b[1][3] = (P[0][0].y-P[0][3].y-P[2][0].y+P[2][3].y + 3*(P[0][2].y-P[0][1].y+P[2][1].y-P[2][2].y))/12; b[1][2] = (-P[0][0].y-P[0][2].y+P[2][0].y+P[2][2].y + 88 2*(P[0][1].y-P[2][1].y))/4; b[1][1] = (P[0][0].y-P[0][2].y-P[2][0].y+P[2][2].y)/4; b[1][0] = (-P[0][0].y-P[0][2].y+P[2][0].y+P[2][2].y + 4*(-P[0][1].y+P[2][1].y))/12; b[0][3] = (P[0][3].y-P[0][0].y-P[2][0].y+P[2][3].y + 3*(P[0][1].y-P[0][2].y+P[2][1].y-P[2][2].y) + 4*(P[1][3].y-P[1][0].y)+12*(P[1][1].y-P[1][2].y))/36; b[0][2] = (P[0][0].y+P[0][2].y+P[2][0].y+P[2][2].y 2*(P[0][1].y+P[2][1].y) + 4*(P[1][0].y+P[1][2].y) 8*P[1][1].y)/12; b[0][1] = (P[0][2].y-P[0][0].y-P[2][0].y+P[2][2].y + 4*(P[1][2].y-P[1][0].y))/12; b[0][0] = (P[0][0].y+P[0][2].y+P[2][0].y+P[2][2].y + 4*(P[0][1].y+P[1][0].y+P[1][2].y+P[2][1].y) + 16*P[1][1].y)/36; c[3][3] = (P[0][0].z-P[0][3].z-P[3][0].z+P[3][3].z + 3*(-P[0][1].z+P[0][2].z-P[1][0].z+P[1][3].z+P[2][0].zP[2][3].z+P[3][1].z-P[3][2].z) + 9*(P[1][1].z-P[1][2].z-P[2][1].z+P[2][2].z))/36; c[3][2] = (-P[0][0].z-P[0][2].z+P[3][0].z+P[3][2].z + 2*(P[0][1].z-P[3][1].z) + 3*(P[1][0].z+P[1][2].z-P[2][0].zP[2][2].z) + 6*(-P[1][1].z+P[2][1].z))/12; c[3][1] = (P[0][0].z-P[0][2].z-P[3][0].z+P[3][2].z + 3*(-P[1][0].z+P[1][2].z+P[2][0].z-P[2][2].z))/12; c[3][0] = (-P[0][0].z-P[0][2].z+P[3][0].z+P[3][2].z + 3*(P[1][0].z+P[1][2].z-P[2][0].z-P[2][2].z)+ 4*(-P[0][1].z+P[3][1].z) + 12*(P[1][1].z-P[2][1].z))/36; c[2][3] = (-P[0][0].z+P[0][3].z-P[2][0].z+P[2][3].z + 2*(P[1][0].z-P[1][3].z)+ 3*(P[0][1].z-P[0][2].z+P[2][1].zP[2][2].z) + 6*(P[1][2].z-P[1][1].z))/12; c[2][2] = (P[0][0].z+P[0][2].z+P[2][0].z+P[2][2].z + 2*(-P[0][1].zP[1][0].z-P[1][2].z-P[2][1].z))/4 + P[1][1].z; c[2][1] = (P[0][2].z-P[0][0].z-P[2][0].z+P[2][2].z + 2*(P[1][0].z-P[1][2].z))/4; c[2][0] = (P[0][0].z+P[0][2].z+P[2][0].z+P[2][2].z 2*(P[1][0].z+P[1][2].z) + 4*(P[0][1].z+P[2][1].z) 8*P[1][1].z)/12; c[1][3] = (P[0][0].z-P[0][3].z-P[2][0].z+P[2][3].z + 3*(P[0][2].z-P[0][1].z+P[2][1].z-P[2][2].z))/12; c[1][2] = (-P[0][0].z-P[0][2].z+P[2][0].z+P[2][2].z + 89 2*(P[0][1].z-P[2][1].z))/4; c[1][1] = (P[0][0].z-P[0][2].z-P[2][0].z+P[2][2].z)/4; c[1][0] = (-P[0][0].z-P[0][2].z+P[2][0].z+P[2][2].z + 4*(-P[0][1].z+P[2][1].z))/12; c[0][3] = (P[0][3].z-P[0][0].z-P[2][0].z+P[2][3].z + 3*(P[0][1].z-P[0][2].z+P[2][1].z-P[2][2].z)+ 4*(P[1][3].z-P[1][0].z)+12*(P[1][1].z-P[1][2].z))/36; c[0][2] = (P[0][0].z+P[0][2].z+P[2][0].z+P[2][2].z 2*(P[0][1].z+P[2][1].z) + 4*(P[1][0].z+P[1][2].z) 8*P[1][1].z)/12; c[0][1] = (P[0][2].z-P[0][0].z-P[2][0].z+P[2][2].z + 4*(P[1][2].z-P[1][0].z))/12; c[0][0] = (P[0][0].z+P[0][2].z+P[2][0].z+P[2][2].z + 4*(P[0][1].z+P[1][0].z+P[1][2].z+P[2][1].z) + 16*P[1][1].z)/36; /* In the output file we will be using point numbers k, which are to be computed from the controlled variables i, j, I, J. In each rectangle, determined by the pair (i, j) and to be divided into N x M elements, we use I, counting up to N, and J, counting up to M. To avoid duplicated points, I and J normally start at I, except for the lowest values (2) of i and j; then I and J start at 0, respectively. (Of all rectangles that are to be divided into elements, the one with i=j=2 is the leftmost rectangle at the top.) If I=0 (which implies i=2), then k = (j-2)M+J+1, otherwise k = A+ (i-2)NA + (I-1)A + (j-2)M + J+ 1. This means that in either case we have: k = {(i-2)N + I}A + {(j-2)M +1}+J (A is the total number of mesh points on a horizontal row. ) */ B = (j-2)*M+1; for (I = (i == 2 ? 0 : 1);I <= N; I++) { u = float(I)/N; u2 = u*u; u3 = u2*u; C = ((i-2)*N+I)*A + B; for (J = (j == 2 ? 0: 1); J <= M; J++) { k = C + J; v = float(J)/M; T.x = Horner(v, a[0][3], a[0][2], a[0][1], a[0][0]) + 90 u * Horner(v, a[1][3], a[1][2], a[1][1], a[1][0])+ u2 * Horner(v, a[2][3], a[2][2], a[2][1], a[2][0])+ u3 * Horner(v, a[3][3], a[3][2], a[3][1], a[3][0]); T.y = Horner(v, b[0][3], b[0][2], b[0][1], b[0][0]) + u * Horner(v, b[1][3], b[1][2], b[1][1], b[1][0])+ u2 * Horner(v, b[2][3], b[2][2], b[2][1], b[2][0])+ u3 * Horner(v, b[3][3], b[3][2], b[3][1], b[3][0]); T.z = Horner(v, c[0][3], c[0][2], c[0][1], c[0][0]) + u * Horner(v, c[1][3], c[1][2], c[1][1], c[1][0])+ u2 * Horner(v, c[2][3], c[2][2], c[2][1], c[2][0])+ u3 * Horner(v, c[3][3], c[3][2], c[3][1], c[3][0]); fprintf(fp2, "%d %f %f %f\n", k, T.x, T.y, T.z); } } } fprintf ( fp2, "Faces: \n" ); nn = (n-3) * N; mm = A-1; /* mm = (m-3)*M */ for (ii = 0; ii < nn; ii++) for (jj = 0; jj < mm; jj++) { p = ii * A + jj + 1; /* We have to divide each (nearly rectangular) surface element into two triangles to ensure that the points that will be used as vertices of polygons really lie in the same plane. The minus sign in, for example, p -Q R will prevent D3D from drawing line segment PQ. Since either face of each triangle may be visible, we specify its vertices both counter-clockwise and clockwise: */ fprintf(fp2, "%d %d %d#\n", p, -(p+A+1), p+1); fprintf(fp2, "%d %d %d#\n", p+A+1, -p, p+A); fprintf(fp2, "%d %d %d#\n", p, -(p+A+1), p+A); fprintf(fp2, "%d %d %d#\n", p+A+1, -p, p+1); } fclose(fp2); } 91 參考文獻: 1. L. Ammeraal, Programming Principles in Computer Graphics, John Wiley & Sons, 1986. 2. L. Ammeraal, Interactive 3D Computer Graphics, John Wiley & Sons, 19886. 92