Beispiel #1
0
/*!*****************************************************************************
 *******************************************************************************
\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;
  
}
Beispiel #2
0
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;
}