/*!***************************************************************************** ******************************************************************************* \note parm_opt \date 10/20/91 \remarks this is the major optimzation program ******************************************************************************* Function Parameters: [in]=input,[out]=output \param[in] tol : error tolernance to be achieved \param[in] n_parm : number of parameters to be optimzed \param[in] n_con : number of contraints to be taken into account \param[in] f_dLda : function which calculates the derivative of the optimziation criterion with respect to the parameters; must return vector \param[in] f_dMda : function which calculates the derivate of the constraints with respect to parameters must return matrix \param[in] f_M : constraints function, must always be formulted to return 0 for properly fulfilled constraints \param[in] f_L : function to calculate simple cost (i.e., constraint cost NOT included), the constraint costs are added by this program automatically, the function returns a double scalar \param[in] f_dLdada : second derivative of L with respect to the parameters, must be a matrix of dim n_parm x n_parm \param[in] f_dMdada : second derivative of M with respect to parameters, must be a matrix n_con*n_parm x n_parm \param[in] use_newton: TRUE or FALSE to indicate that second derivatives are given and can be used for Newton algorithm \param[in,out] a : initial setting of parameters and also return of optimal value (must be a vector, even if scalar) \param[out] final_cost: the final cost \param[out] err : the sqrt of the squared error of all constraints NOTE: - program returns TRUE if everything correct, otherwise FALSE - always minimizes the cost!!! - algorithms come from Dyer McReynolds NOTE: besides the possiblity of a bug, the Newton method seems to sacrifice the validity of the constraint a little up to quite a bit and should be used prudently ******************************************************************************/ int parm_opt(double *a,int n_parm, int n_con, double *tol, void (*f_dLda)(), void (*f_dMda)(), void (*f_M)(), double (*f_L)(), void (*f_dMdada)(), void (*f_dLdada)(), int use_newton, double *final_cost, double *err) { register int i,j,n; double cost= 999.e30; double last_cost = 0.0; double *mult=NULL, *new_mult=NULL; /* this is the vector of Lagrange mulitplier */ double **dMda=NULL, **dMda_t=NULL; double *dLda; double *K=NULL; /* the error in the constraints */ double eps = 0.025; /* the learning rate */ double **aux_mat=NULL; /* needed for inversion of matrix */ double *aux_vec=NULL; double *new_a; double **dMdada=NULL; double **dLdada=NULL; double **A=NULL; /* big matrix, a combination of several other matrices */ double *B=NULL; /* a big vector */ double **A_inv=NULL; int rc=TRUE; long count = 0; int last_sign = 1; int pending1 = FALSE, pending2 = FALSE; int firsttime = TRUE; int newton_active = FALSE; dLda = my_vector(1,n_parm); new_a = my_vector(1,n_parm); if (n_con > 0) { mult = my_vector(1,n_con); dMda = my_matrix(1,n_con,1,n_parm); dMda_t = my_matrix(1,n_parm,1,n_con); K = my_vector(1,n_con); aux_mat = my_matrix(1,n_con,1,n_con); aux_vec = my_vector(1,n_con); } if (use_newton) { dLdada = my_matrix(1,n_parm,1,n_parm); A = my_matrix(1,n_parm+n_con,1,n_parm+n_con); A_inv = my_matrix(1,n_parm+n_con,1,n_parm+n_con); B = my_vector(1,n_parm+n_con); if (n_con > 0) { dMdada = my_matrix(1,n_con*n_parm,1,n_parm); new_mult = my_vector(1,n_con); } for (i=1+n_parm; i<=n_con+n_parm; ++i) { for (j=1+n_parm; j<=n_con+n_parm; ++j) { A[i][j] = 0.0; } } } while (fabs(cost-last_cost) > *tol) { ++count; pending1 = FALSE; pending2 = FALSE; AGAIN: /* calculate the current Lagrange multipliers */ if (n_con > 0) { (*f_M)(a,K); /* takes the parameters, returns residuals */ (*f_dMda)(a,dMda); /* takes the parameters, returns the Jacobian */ } (*f_dLda)(a,dLda); /* takes the parameters, returns the gradient */ if (n_con > 0) { mat_trans(dMda,dMda_t); } if (newton_active) { if (n_con > 0) { (*f_dMdada)(a,dMdada); } (*f_dLdada)(a,dLdada); } /* the first step is always a gradient step */ if (newton_active) { if (firsttime) { firsttime = FALSE; eps = 0.1; } /* build the A matrix */ for (i=1; i<=n_parm; ++i) { for (j=1; j<=n_parm; ++j) { A[i][j] = dLdada[i][j]; for (n=1; n<=n_con; ++n) { A[i][j] += mult[n]*dMdada[n+(i-1)*n_con][j]; } } } for (i=1+n_parm; i<=n_con+n_parm; ++i) { for (j=1; j<=n_parm; ++j) { A[j][i] = A[i][j] = dMda[i-n_parm][j]; } } /* build the B vector */ if (n_con > 0) { mat_vec_mult(dMda_t,mult,B); } for (i=1; i<=n_con; ++i) { B[i+n_parm] = K[i]; } /* invert the A matrix */ if (!my_inv_ludcmp(A, n_con+n_parm, A_inv)) { rc = FALSE; break; } mat_vec_mult(A_inv,B,B); vec_mult_scalar(B,eps,B); for (i=1; i<=n_parm; ++i) { new_a[i] = a[i] + B[i]; } for (i=1; i<=n_con; ++i) { new_mult[i] = mult[i] + B[n_parm+i]; } } else { if (n_con > 0) { /* the mulitpliers are updated according: mult = (dMda dMda_t)^(-1) (K/esp - dMda dLda_t) */ mat_mult(dMda,dMda_t,aux_mat); if (!my_inv_ludcmp(aux_mat, n_con, aux_mat)) { rc = FALSE; break; } mat_vec_mult(dMda,dLda,aux_vec); vec_mult_scalar(K,1./eps,K); vec_sub(K,aux_vec,aux_vec); mat_vec_mult(aux_mat,aux_vec,mult); } /* the update step looks the following: a_new = a - eps * (dLda + mult_t * dMda)_t */ if (n_con > 0) { vec_mat_mult(mult,dMda,new_a); vec_add(dLda,new_a,new_a); } else { vec_equal(dLda,new_a); } vec_mult_scalar(new_a,eps,new_a); vec_sub(a,new_a,new_a); } if (count == 1 && !pending1) { last_cost = (*f_L)(a); if (n_con > 0) { (*f_M)(a,K); last_cost += vec_mult_inner(K,mult); } } else { last_cost = cost; } /* calculate the updated cost */ cost = (*f_L)(new_a); /*printf(" %f\n",cost);*/ if (n_con > 0) { (*f_M)(new_a,K); if (newton_active) { cost += vec_mult_inner(K,new_mult); } else { cost += vec_mult_inner(K,mult); } } /* printf("last=%f new=%f\n",last_cost,cost); */ /* check out whether we reduced the cost */ if (cost > last_cost && fabs(cost-last_cost) > *tol) { /* reduce the gradient climbing rate: sometimes a reduction of eps causes an increase in cost, thus leave an option to increase eps */ cost = last_cost; /* reset last_cost */ if (pending1 && pending2) { /* this means that either increase nor decrease of eps helps, ==> leave the program */ rc = TRUE; break; } else if (pending1) { eps *= 4.0; /* the last cutting by half did not help, thus multiply by 2 to get to previous value, and one more time by 2 to get new value */ pending2 = TRUE; } else { eps /= 2.0; pending1 = TRUE; } goto AGAIN; } else { vec_equal(new_a,a); if (newton_active && n_con > 0) { vec_equal(new_mult,mult); } if (use_newton && fabs(cost-last_cost) < NEWTON_THRESHOLD) newton_active = TRUE; } } my_free_vector(dLda,1,n_parm); my_free_vector(new_a,1,n_parm); if (n_con > 0) { my_free_vector(mult,1,n_con); my_free_matrix(dMda,1,n_con,1,n_parm); my_free_matrix(dMda_t,1,n_parm,1,n_con); my_free_vector(K,1,n_con); my_free_matrix(aux_mat,1,n_con,1,n_con); my_free_vector(aux_vec,1,n_con); } if (use_newton) { my_free_matrix(dLdada,1,n_parm,1,n_parm); my_free_matrix(A,1,n_parm+n_con,1,n_parm+n_con); my_free_matrix(A_inv,1,n_parm+n_con,1,n_parm+n_con); my_free_vector(B,1,n_parm+n_con); if (n_con > 0) { my_free_matrix(dMdada,1,n_con*n_parm,1,n_parm); my_free_vector(new_mult,1,n_con); } } *final_cost = cost; *tol = fabs(cost-last_cost); if (n_con > 0) { *err = sqrt(vec_mult_inner(K,K)); } else { *err = 0.0; } /* printf("count=%ld rc=%d\n",count,rc); */ return rc; }
int main(int argc, char** argv) { if(argc >= 2) { if(strcmp(argv[1], "--help") == 0) { print_usage(); exit(2); } } int c; int errflg = 0; int male = 1; float D = 9.0; // default value, can be changed as a command line argument float P = 2; // default value, can be changed as a command line argument float theta = M_PI/3; // 60 degrees, the standard, can be changed as a command line argument float screwHeight = 20; // height of entire screw (without a head) // TODO maybe add ability to add a head float outerDiameter = -1; int segments = 72; // number of segments to approximate a circle, // higher the number, higher the resolution // (and file size) of the stl file while((c = getopt(argc, argv, "fP:D:a:h:s:o:")) != -1) { switch(c) { case 'f': male = 0; break; case 'P': P = atof(optarg); break; case 'D': D = atof(optarg); break; case 'a': theta = atof(optarg)*M_PI/180; break; case 'h': screwHeight = atof(optarg); break; case 's': segments = atoi(optarg); break; case 'o': outerDiameter = atof(optarg); break; case '?': fprintf(stderr, "Unrecognized option: '-%c'\n", optopt); errflg++; break; } } if(outerDiameter <= D) { outerDiameter = D+1; } if(errflg || optind >= argc) { print_usage(); exit(2); } char *file = argv[optind]; FILE *outf; outf = fopen(file, "wb"); if(!outf) { fprintf(stderr, "Can't write to file: %s\n", file); exit(2); } char header[81] = {0}; // TODO add some settings summary to header string snprintf(header, 81, "Created with stl_threads."); fwrite(header, 80, 1, outf); // writing 0 to num_tris for now, will update it at the end. uint32_t num_tris = 0; fwrite(&num_tris, 4, 1, outf); // ISO metric screw thread standard designates screw threads // as M followed by diameter D, a multiplication sign, and then // the pitch P (e.g. M8x1.25). Other standards designate standard // pitches for a given diameter (e.g. M8 means the same as M8x1.25). // // See http://en.wikipedia.org/wiki/ISO_metric_screw_thread // // The standard assumes a theta of 60 degrees, but we allow // theta to change, for ease of manufacturing. Certain 3D printers // may not be able to print without drooping at 60 degrees. int female = !male; // equations vary from wikipedia because we're not assuming a 60 degree theta // and we're allowing cutting off different amounts than H/8 // and H/4 from the tip and troughs float tantheta_2 = tan(theta/2); float H = P/(2*tantheta_2); float Htip = H/8; float Htrough = H/4; float Hdiff = H-Htip-Htrough; float Pdiff = Hdiff*tantheta_2; float Ptip = 2*Htip*tantheta_2; float Ptrough = 2*Htrough*tantheta_2; float Dmin = D-2*Hdiff; float Dmin_2 = Dmin/2; float D_2 = D/2; float fD = outerDiameter/2; /* // my ascii representation of image at // (http://en.wikipedia.org/wiki/ISO_metric_screw_thread) // with my own added variables |<--H->| | | | | | |-------------. pt5| ------ // pt5 is the same as pt1 one cycle later | ^ |/| | ^ | | . | | Ptrough | | \| | v | | . pt4| ------ | | \ | | | ( \ | | ( . pt3 ------ | P ( |\ ^ | theta | . Ptip | | ( |/ v | | ( . pt2 ------ | | ( / ^ | v / | Pdiff |-------------. pt1 ------ | /| | | . | | |<---Dmin/2-->| | | | | | |<-----D/2------>| */ vec pt1,pt2,pt3,pt4,pt5; pt1.x = Dmin_2; pt1.y = 0; pt1.z = 0; pt1.w = 1; pt2.x = D_2; pt2.y = 0; pt2.z = Pdiff; pt2.w = 1; pt3.x = D_2; pt3.y = 0; pt3.z = Pdiff+Ptip; pt3.w = 1; pt4.x = Dmin_2; pt4.y = 0; pt4.z = 2*Pdiff+Ptip; pt4.w = 1; pt5.x = Dmin_2; // not used, instead pt1 one full cycle later is used pt5.y = 0; // to avoid floating point errors pt5.z = P; pt5.w = 1; int total_segments = (screwHeight/P-1)*segments; vec origin = {0}; vec top = {0}; top.z = screwHeight; vec up = {0}; up.z = 1; vec down = {0}; down.z = -1; float anginc = (float)360/segments; vec sliced8Int; int needsExtraTris = 0; for(int i = -segments; i < total_segments+segments; i++) { vec p1,p2,p3,p4,p5; vec p1n,p2n,p3n,p4n,p5n; vec ob1,ob1n; vec ot1,ot1n; float ango = (float)360*((i+segments)%segments)/segments; float angno = (float)360*((i+segments)%segments+1)/segments; float cosao = cos(ango*M_PI/180); float sinao = sin(ango*M_PI/180); float cosano = cos(angno*M_PI/180); float sinano = sin(angno*M_PI/180); ob1.x = fD*cosao; ob1.y = fD*sinao; ob1.z = 0; ob1n.x = fD*cosano; ob1n.y = fD*sinano; ob1n.z = 0; ot1.x = fD*cosao; ot1.y = fD*sinao; ot1.z = screwHeight; ot1n.x = fD*cosano; ot1n.y = fD*sinano; ot1n.z = screwHeight; float ang = (float)360*i/segments; float angn = (float)360*(i+1)/segments; float angc = (float)360*(i+segments)/segments; float angnc = (float)360*(i+1+segments)/segments; float cosa = cos(ang*M_PI/180); float sina = sin(ang*M_PI/180); float cosan = cos(angn*M_PI/180); float sinan = sin(angn*M_PI/180); float cosanc = cos(angnc*M_PI/180); float sinanc = sin(angnc*M_PI/180); float cosac = cos(angc*M_PI/180); float sinac = sin(angc*M_PI/180); float z = P*ang/360; float zn = P*angn/360; float zc = P*angc/360; float znc = P*angnc/360; mat t; mat tn; mat tc; mat tnc; t.xx = cosa; t.xy = sina; t.xz = 0; t.xw = 0; t.yx = -sina; t.yy = cosa; t.yz = 0; t.yw = 0; t.zx = 0; t.zy = 0; t.zz = 1; t.zw = 0; t.tx = 0; t.ty = 0; t.tz = z; t.tw = 1; tn.xx = cosan; tn.xy = sinan; tn.xz = 0; tn.xw = 0; tn.yx = -sinan; tn.yy = cosan; tn.yz = 0; tn.yw = 0; tn.zx = 0; tn.zy = 0; tn.zz = 1; tn.zw = 0; tn.tx = 0; tn.ty = 0; tn.tz = zn; tn.tw = 1; tnc.xx = cosanc; tnc.xy = sinanc; tnc.xz = 0; tnc.xw = 0; tnc.yx = -sinanc; tnc.yy = cosanc; tnc.yz = 0; tnc.yw = 0; tnc.zx = 0; tnc.zy = 0; tnc.zz = 1; tnc.zw = 0; tnc.tx = 0; tnc.ty = 0; tnc.tz = znc; tnc.tw = 1; tc.xx = cosac; tc.xy = sinac; tc.xz = 0; tc.xw = 0; tc.yx = -sinac; tc.yy = cosac; tc.yz = 0; tc.yw = 0; tc.zx = 0; tc.zy = 0; tc.zz = 1; tc.zw = 0; tc.tx = 0; tc.ty = 0; tc.tz = zc; tc.tw = 1; vec_mat_mult(&pt1, &t, &p1); vec_mat_mult(&pt2, &t, &p2); vec_mat_mult(&pt3, &t, &p3); vec_mat_mult(&pt4, &t, &p4); vec_mat_mult(&pt1, &tc, &p5); // using pt1 with a transform of 1 // full cycle around to avoid // floating point roundoff errors vec_mat_mult(&pt1, &tn, &p1n); vec_mat_mult(&pt2, &tn, &p2n); vec_mat_mult(&pt3, &tn, &p3n); vec_mat_mult(&pt4, &tn, &p4n); vec_mat_mult(&pt1, &tnc, &p5n); // using pt1 with a transform of 1 // full cycle around to avoid // floating point roundoff errors if(i < 0) { int wrote_outer_tri = 0; vec int1,int2; int tris_written; if(write_sliced_tri(outf, &p1,&p1n,&p2n,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,0); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p1,&p2n,&p2,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p2,&p2n,&p3n,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p2,&p3n,&p3,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p3,&p3n,&p4n,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p3,&p4n,&p4,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p4,&p4n,&p5n,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p4,&p5n,&p5,female,&origin,&up,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int2,&origin,&int1,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ob1,0); num_tris += 1; } else { write_tri(outf,&int1,&ob1n,&ob1,0); write_tri(outf,&int2,&int1,&ob1,0); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; } else if(i >= total_segments) { int wrote_outer_tri = 0; vec int1,int2; int tris_written; if(write_sliced_tri(outf, &p1,&p1n,&p2n,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } if(i == total_segments+segments-1 && needsExtraTris) { write_tri(outf, &int1,&p1n,&sliced8Int,female); num_tris += 1; if(male) { write_tri(outf, &int1,&sliced8Int,&top,female); num_tris += 1; } else { write_tri(outf, &int1,&sliced8Int,&ot1n,female); num_tris += 1; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p1,&p2n,&p2,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p2,&p2n,&p3n,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p2,&p3n,&p3,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p3,&p3n,&p4n,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p3,&p4n,&p4,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p4,&p4n,&p5n,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } } num_tris += tris_written; if(write_sliced_tri(outf, &p4,&p5n,&p5,female,&top,&down,&int1,&int2,&tris_written)) { if(male) { write_tri(outf,&int1,&top,&int2,female); num_tris += 1; } else { if(wrote_outer_tri) { write_tri(outf,&int2,&int1,&ot1,1); num_tris += 1; } else { write_tri(outf,&int1,&ot1n,&ot1,1); write_tri(outf,&int2,&int1,&ot1,1); wrote_outer_tri = 1; num_tris += 2; } } if(i == total_segments && !vec_equals(&int2,&p5)) { needsExtraTris = 1; vec_copy(&int2,&sliced8Int); } } num_tris += tris_written; } else { write_quad(outf, &p1,&p1n,&p2n,&p2,female); write_quad(outf, &p2,&p2n,&p3n,&p3,female); write_quad(outf, &p3,&p3n,&p4n,&p4,female); write_quad(outf, &p4,&p4n,&p5n,&p5,female); num_tris += 8; } } if(female) { for(int i = 0; i < segments; i++) { vec ob1,ob1n; vec ot1,ot1n; float ang = (float)360*i/segments; float angn = (float)360*(i+1)/segments; float cosa = cos(ang*M_PI/180); float sina = sin(ang*M_PI/180); float cosan = cos(angn*M_PI/180); float sinan = sin(angn*M_PI/180); ob1.x = fD*cosa; ob1.y = fD*sina; ob1.z = 0; ob1n.x = fD*cosan; ob1n.y = fD*sinan; ob1n.z = 0; ot1.x = fD*cosa; ot1.y = fD*sina; ot1.z = screwHeight; ot1n.x = fD*cosan; ot1n.y = fD*sinan; ot1n.z = screwHeight; write_quad(outf,&ob1,&ob1n,&ot1n,&ot1,0); num_tris += 2; } } fseek(outf, 80, SEEK_SET); fwrite(&num_tris, 4, 1, outf); fclose(outf); return 0; }