Ejemplo n.º 1
0
void Vectorizer::process_triangle(int iv0, int iv1, int iv2, int level,
                                  scalar* xval, scalar* yval, double* phx, double* phy, int* idx)
{
  if (level < LIN_MAX_LEVEL)
  {
    int i;
    if (!(level & 1))
    {
      // obtain solution values and physical element coordinates
      xsln->set_quad_order(1, xitem);
      ysln->set_quad_order(1, yitem);
      xval = xsln->get_values(xia, xib);
      yval = ysln->get_values(yia, yib);
      for (i = 0; i < lin_np_tri[1]; i++) {
        double m = getmag(i);
        if (finite(m) && fabs(m) > max) max = fabs(m);
      }

      if (curved)
      {
        RefMap* refmap = xsln->get_refmap();
        phx = refmap->get_phys_x(1);
        phy = refmap->get_phys_y(1);
      }
      idx = tri_indices[0];
    }

    // obtain linearized values and coordinates at the midpoints
    double midval[4][3];
    for (i = 0; i < 4; i++)
    {
      midval[i][0] = (verts[iv0][i] + verts[iv1][i])*0.5;
      midval[i][1] = (verts[iv1][i] + verts[iv2][i])*0.5;
      midval[i][2] = (verts[iv2][i] + verts[iv0][i])*0.5;
    };

    // determine whether or not to split the element
    bool split;
    if (eps >= 1.0)
    {
      //if eps > 1, the user wants a fixed number of refinements (no adaptivity)
      split = (level < eps);
    }
    else
    {
      // calculate the approximate error of linearizing the normalized solution
      double err = fabs(getmag(idx[0]) - midmag(0)) +
                   fabs(getmag(idx[1]) - midmag(1)) +
                   fabs(getmag(idx[2]) - midmag(2));
      split = !finite(err) || err > max*3*eps;

      // do the same for the curvature
      if (curved && !split)
      {
        double cerr = 0.0, cden = 0.0; // fixme
        for (i = 0; i < 3; i++)
        {
          cerr += fabs(phx[idx[i]] - midval[0][i]) + fabs(phy[idx[i]] - midval[1][i]);
          cden += fabs(phx[idx[i]]) + fabs(phy[idx[i]]);
        }
        split = (cerr > cden*2.5e-4);
      }

      // do extra tests at level 0, so as not to miss some functions with zero error at edge midpoints
      if (level == 0 && !split)
      {
        split = (fabs(getmag(8) - 0.5*(midmag(0) + midmag(1))) +
                 fabs(getmag(9) - 0.5*(midmag(1) + midmag(2))) +
                 fabs(getmag(4) - 0.5*(midmag(2) + midmag(0)))) > max*3*eps;
      }
    }

    // split the triangle if the error is too large, otherwise produce a linear triangle
    if (split)
    {
      if (curved)
        for (i = 0; i < 3; i++) {
          midval[0][i] = phx[idx[i]];
          midval[1][i] = phy[idx[i]];
        }

      // obtain mid-edge vertices
      int mid0 = get_vertex(iv0, iv1, midval[0][0], midval[1][0], getvalx(idx[0]), getvaly(idx[0]));
      int mid1 = get_vertex(iv1, iv2, midval[0][1], midval[1][1], getvalx(idx[1]), getvaly(idx[1]));
      int mid2 = get_vertex(iv2, iv0, midval[0][2], midval[1][2], getvalx(idx[2]), getvaly(idx[2]));

      // recur to sub-elements
      push_transform(0);  process_triangle(iv0, mid0, mid2,  level+1, xval, yval, phx, phy, tri_indices[1]);  pop_transform();
      push_transform(1);  process_triangle(mid0, iv1, mid1,  level+1, xval, yval, phx, phy, tri_indices[2]);  pop_transform();
      push_transform(2);  process_triangle(mid2, mid1, iv2,  level+1, xval, yval, phx, phy, tri_indices[3]);  pop_transform();
      push_transform(3);  process_triangle(mid1, mid2, mid0, level+1, xval, yval, phx, phy, tri_indices[4]);  pop_transform();
      return;
    }
  }

  // no splitting: output a linear triangle
  add_triangle(iv0, iv1, iv2);
}
Ejemplo n.º 2
0
int main(void){

	int integration[3] = {0,0,0};

	char lostsignalcnt = 0;

	int pry[] = {0,0,0};

	int paceCounter = 0;

	int pidValues13[3] = {6,20,24};
	int pidValuesDen13[3] = {16,1,1};

	int pidValues24[3] = {6,20,24};
	int pidValuesDen24[3] = {16,1,1};

	char pidRotUp[3] = {0,0,20};
	char pidRotDenUp[3] = {42,1,1};
	
	char pidRotDown[3] = {9,0,20};
	char pidRotDenDown[3] = {42,1,1};

	char pidRot[] = {5,0,20};

	int throttledif = 0;
	int throttleavr = 0;

	/*counting var, for for loops*/
	int i;


	/*Start memory location for Accel and Gyro reads, should be moved
	  to gyro and accel read functions*/
	uint8_t accelstartbyte = 0x30;
	uint8_t gyrostartbyte = 0x1A;

	/*Joystick Axis buffer
	  [0] - X axis tilt
	  [1] - Y axis tilt
	  [2] - Throttle
	  [3] - Rotation about Z axis
	 */
	int joyaxis[] = {0,0,0,0,0};
	char joyin[] = {0,0,0,0,0};
	int joytrim[] = {0,0,0,0,0};
	int joydif[] = {0,0};
	int joyavr[] = {0,0};
	int motorSpeeds[4];

	/*Var to allow increase in motor speed nonrelative to the throttle
	  during flight*/
	int motorup = 0;

	/*Vars for new input raw data (cache) and filtered data (int) from
	  imu*/
	int gyrocache[3] = {0,0,0};
	int accelcache[3] = {0,0,0};
	int magcache[3] = {0,0,0};
	int magfacing = 0;
	int roterr = 0;
	int target[] = {0,0,0};
	int accelint[] = {0, 0, 0};
	int gyroint[] = {0, 0, 0};
	int gyrocounter[] = {0,0,0};


	/*Standard values for accel and gyro (when level), set during offset*/
	int accelnorm[3] = {28,-20,468};
	char gyronorm[3] = {16,42,0};

	/*Buffer for sending data through the xbee*/
	char xbeebuffer[100];


	CLK.CTRL = 0b00000011;
	CLK.PSCTRL = 0b00010100;

	/*Initialize PORTD to output on pins 0-3 from Timer counter pwm at
	  50Hz*/
	PORTD.DIR = 0x2F;
	TCD0.CTRLA = TC_CLKSEL_DIV1_gc;
	TCD0.CTRLB = TC_WGMODE_SS_gc | TC0_CCCEN_bm |  TC0_CCAEN_bm |TC0_CCBEN_bm | TC0_CCDEN_bm;
	TCD0.PER = 8000;

	/*Initialize Timer counter C0 for pacing,RATE Hz*/
	TCC0.CTRLA = TC_CLKSEL_DIV1_gc;
	TCC0.CTRLB = TC_WGMODE_SS_gc;
	TCC0.PER = 2000000 / RATE;
	/*Set on board LED pins to output*/
	PORTF.DIR = 0x03;

	/*Set PORTC to power IMU, PIN 3 3.3V, pin 2 ground*/
	PORTC.DIR = 0b00001100;
	PORTC.OUT = 0b00001000;

	/*Enable global interrupts of all priority levels, should be made
	  more relevant*/
	PMIC.CTRL |= PMIC_LOLVLEX_bm | PMIC_MEDLVLEX_bm | PMIC_HILVLEX_bm |
		PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
	sei();

	/*Set pwm duty cycle to stop motors, stop them from beeping 
	  annoyingly*/
	TCD0.CCA = 2000;
	TCD0.CCB = 2000;
	TCD0.CCC = 2000;
	TCD0.CCD = 2000;


	/*Set Xbee Uart transmit pin 3 to output*/
	PORTE.DIR = 0x08;
	/*Initialize USARTE0 as the module used by the Xbee*/
	uartInitiate(&xbee, &USARTE0);
	
	/*Initialize USARTE1 as the module used by atmega328p */
	uartInitiate(&atmega328p, &USARTE1);

	/*Initialize imu to use Two wire interface on portC*/
	twiInitiate(&imu, &TWIC);
	itg3200Init(&imu, RATE);
	adxl345Init(&imu);
	lsm303dlhInit(&imu);

	/*Send string to indicate startup, python doesn't like return carriage
	  (/r) character*/
	sprintf(xbeebuffer, "starting\n");
	sendstring(&xbee, xbeebuffer);

	/*Start of flight control state machine loop*/
	while(1){	
		
		/*Check for new packet from atmega328p*/
		if(IRDataAvailable)
		{
			sprintf(xbeebuffer, "Altitude: %d\n", (irdata[1]*256) + irdata[2]);
			sendstring(&xbee, xbeebuffer);

		}

		/*Check for new packet from xbee each time*/
		if(readdata){
			readdata = 0;
			lostsignalcnt = 0;


			/*For Joystick packet reads*/

			joytrim[2] = 0;
			for(i = 0; i < 5; i++){
				joyin[i] = -input[3 + i] + 126;
				joyaxis[i] = joyin[i];
				joyaxis[i] += joytrim[i];
			}

			throttleavr = ((throttleavr) + (joyaxis[2]))/2;
			throttledif = joyaxis[2] - throttleavr;
			joyaxis[2] += throttledif * THROTTLEJOYDIF;

			for(i = 0; i < 2; i++){
				joyavr[i] = (joyavr[i] + joyaxis[i])/2;
				joydif[i] = joyaxis[i] - joyavr[i];
			}

			joyaxis[1] += joydif[1] * PRJOYDIF13;
			joyaxis[0] += joydif[0] * PRJOYDIF24;
			
		

/*
			yawavr = ((yawavr) + joyaxis[3])/2;
			yawdif = joyaxis[3] - yawavr;
			joyaxis[3] += yawdif * YAWJOYDIF;
*/
			//Input 7 is the button buffer
			if(input[8] == 4){
				state = stopped;
				//sprintf(xbeebuffer, "stopped %d\n", input[7]);
				sprintf(xbeebuffer, "%4d %4d %4d %4d\n", joyaxis[0], joyaxis[1], joyaxis[2], joyaxis[3]);
				sendstring(&xbee, xbeebuffer);
			}
			else if(input[8] == 0){
				joytrim[0] += joyin[0];
				joytrim[1] += joyin[1];
				joytrim[3] += joyin[3];
			}
			else if(input[8] == 1){
				state = running;
				sprintf(xbeebuffer, "running %d\n", input[7]);
				sendstring(&xbee, xbeebuffer);
			}
			else if(input[8] == 10){
				state = offset;
			}
			else if(input[8] == 5){
				//motorup += 5;
				pidRot[2] ++;
				sprintf(xbeebuffer, "D up %d\n", pidRot[2]);
				//pidRot[2] ++;
				//sprintf(xbeebuffer, "D up %d\n", pidValues24[2]);

				sendstring(&xbee, xbeebuffer);

			}
			else if(input[8] == 6){
				pidRot[2] --;
				sprintf(xbeebuffer, "D down %d\n", pidRot[2]);
				//pidRot[2] --;
				//sprintf(xbeebuffer, "D down %d\n", pidValues24[2]);
				sendstring(&xbee, xbeebuffer);
				//motorup -= 5;
			}
			else if(input[8] == 7){
				pidRot[0] ++;
				sprintf(xbeebuffer, "P up %d\n", pidRot[0]);
				//pidRot[0] ++;
				//sprintf(xbeebuffer, "P up %d\n", pidValues24[0]);
				sendstring(&xbee, xbeebuffer);
			}
			else if(input[8] == 8){
				
				   pidRot[0] --;
				   sprintf(xbeebuffer, "P down %d\n", pidRot[0]);
				   //pidRot[0] --;
				//sprintf(xbeebuffer, "P down %d\n", pidValues24[0]);
				sendstring(&xbee, xbeebuffer);
				 
/*
				getmag(magcache, &imu);
				sprintf(xbeebuffer, "%4d %4d %4d\n", magcache[0], magcache[1], magcache[2]);
				sendstring(&xbee, xbeebuffer);
*/

			}
			else if(input[8] == 2){
				sprintf(xbeebuffer, "descending\n");
				sendstring(&xbee, xbeebuffer);
				motorup = -50;
			}
			xbeecounter = 0;

			for(i=0;i<3;i++){
				pidRotUp[i] = pidRot[i] * 3/4;
				pidRotDown[i] = pidRot[i] * 1;
			}

			if(state == running){
				//sprintf(xbeebuffer, "%d %d\n", joyaxis[2], throttledif);
				//sprintf(xbeebuffer, "%d %d %d \n", joyaxis[0], joyaxis[1], joyaxis[3]);
				//sprintf(xbeebuffer, "%4d %4d %4d\n", pry[0], pry[1], pry[2]);
				//sprintf(xbeebuffer, "%3d %3d\n", gyroint[2], joyaxis[3]);
				//sprintf(xbeebuffer, "%4d %4d %4d %4d\n", motorSpeeds[0], motorSpeeds[1], motorSpeeds[2], motorSpeeds[3]);
				//sprintf(xbeebuffer, "%4d %4d %4d\n", accelint[0], accelint[1], accelint[2]);
				//sprintf(xbeebuffer, "%4d %4d %4d\n", magcache[0], magcache[1], magcache[2]);
				sprintf(xbeebuffer, "%4d\n", roterr);
				sendstring(&xbee, xbeebuffer);
			}

		}

		

		switch(state){

			/*Stopped state keeps motors stopped but not beeping*/
			case stopped:
				TCD0.CCA = 2000;
				TCD0.CCB = 2000;
				TCD0.CCC = 2000;
				TCD0.CCD = 2000;
				break;
				/*Offset gets standard value for gyro's and accel's*/
			case offset:
					getgyro(gyrocache, &imu, &gyrostartbyte);
					getaccel(accelcache, &imu, &accelstartbyte);
					getmag(magcache, &imu);
					target[2] = arctan2(magcache[0], magcache[1]);

				for(i = 0; i < 3; i ++){
					gyronorm[i] = gyrocache[i];
					//accelnorm[i] = accelcache[i];
					accelcache[i] = 0;
					gyrocache[i] = 0;
				}
				   sprintf(xbeebuffer, "offset %d %d %d %d %d %d\n", gyronorm[0], gyronorm[1], gyronorm[2], accelnorm[0], accelnorm[1], accelnorm[2]);
				   sendstring(&xbee, xbeebuffer);


				state = stopped;

				break;



			case running:
				/*Ensure loop doesn't go faster than 50Hz*/
				while(!(TCC0.INTFLAGS & 0x01));
				TCC0.INTFLAGS = 0x01;

				/*Get gyro data
				  substract stationary offset
				  filter for stability
				 */
				getgyro(gyrocache, &imu, &gyrostartbyte);
				for(i = 0; i < 3; i ++){
					gyrocache[i] -= gyronorm[i];

					if((gyrocache[i] <= 1) && (gyrocache[i] >= -1)){
						gyrocache[i] = 0;
					}

					gyrocounter[i] += gyrocache[i];
				}

				for(i = 0; i < 3; i += 2){

					gyroint[i] = gyrocounter[i]/DEGREE;
					gyrocounter[i] %= DEGREE;

					pry[i] += gyroint[i];
					if(pry[i] > PI){
						pry[i] = -PI + (pry[i] - PI);
					}
					else if(pry[i] < -PI){
						pry[i] = PI + (pry[i] + PI);
					}

				}
				
				gyroint[1] = -(gyrocounter[1]/DEGREE);
				gyrocounter[1] %= DEGREE;
				pry[1] += gyroint[1];



				paceCounter ++;
				//Slower Operations at 50Hz

				if(paceCounter == (RATE / 20)){
					paceCounter = 0;
					lostsignalcnt ++;
				
					sendbyte(&atmega328p, 'r');	//ask for IR data					

					getaccel(accelcache, &imu, &accelstartbyte);
					getmag(magcache, &imu);
					magfacing = arctan2(magcache[0], magcache[1]);
					
					if((4900 - abs(pry[2]) - abs(target[2])) < abs(pry[2] - target[2])){
						if(target > 0){
							roterr = 4900 - abs(target[2]) - abs(pry[2]);
						}
						else{
							roterr = -(4900 - abs(target[2]) - abs(pry[2]));
						}
					}
					else{
						roterr = target[2] - pry[2];
					}
					

					for(i = 0; i < 3; i ++){
						accelcache[i] -= accelnorm[i];
/*
						if(accelcache[i] > (accelint[i] + 40)){
							accelcache[i] = accelint[i] + 40;
						}
						else if(accelcache[i] < (accelint[i] - 40)){
							accelcache[i] = accelint[i] - 40;

						}
*/
					}


					accelint[0] = ((ACCELINT * accelint[0]) + ((24 - ACCELINT) * accelcache[0]))/24;


					accelint[1] = ((ACCELINT * accelint[1]) + ((24 - ACCELINT) * accelcache[1]))/24;


					if(accelint[1] > (pry[0] + 15)){
						accelint[1] = pry[0] + 15;
					}
					else if(accelint[1] < (pry[0] - 15)){
						accelint[1] = pry[0] - 15;
					}


					if(accelint[0] > (pry[1] + 15)){
						accelint[0] = pry[1] + 15;
					}
					else if(accelint[0] < (pry[1] - 15)){
						accelint[0] = pry[1] - 15;
					}


					pry[0] = ((AWEIGHT * accelint[1]) + (GWEIGHT * pry[0])) / (AWEIGHT + GWEIGHT);

					pry[1] = ((AWEIGHT * accelint[0]) + (GWEIGHT * pry[1])) / (AWEIGHT + GWEIGHT);

					/*reset cache values to 0, should be made unnecessary by modding gyro and
					  accel read functions*/
					for(i = 0; i < 3; i ++){
						accelcache[i] = 0;
					}

				}

					if(gyroint[0] > 6){
						gyroint[0] = 6;
					}
					else if(gyroint[0] < -6){
						gyroint[0] = -6;
					}


					if(gyroint[1] > 6){
						gyroint[1] = 6;
					}
					else if(gyroint[1] < -6){
						gyroint[1] = -6;
					}


				motorSpeed(pry, integration ,gyroint, joyaxis, motorSpeeds, pidValues13, pidValues24, pidValuesDen13, pidValuesDen24);
				yawCorrect(motorSpeeds, gyroint, &roterr,pidRotUp,pidRotDenUp,pidRotDown,pidRotDenDown);


				if(lostsignalcnt > 10){
					for(i = 0; i < 4; i ++){
						motorSpeeds[i] -= 50;
					}
				}


				while(!((TCD0.CNT > 5000) || (TCD0.CNT < 2500)));

				TCD0.CCA = motorSpeeds[0] + motorup;// - motordif13;

				TCD0.CCC = motorSpeeds[2] + motorup;// +  motordif13;
				TCD0.CCB = motorSpeeds[1] + motorup;// + motordif24;
				TCD0.CCD = motorSpeeds[3] + motorup;// - motordif24;




				PORTD.OUT ^= 0b00100000;	


		}

	}
}
Ejemplo n.º 3
0
void Vectorizer::process_quad(int iv0, int iv1, int iv2, int iv3, int level,
                              scalar* xval, scalar* yval, double* phx, double* phy, int* idx)
{
  // try not to split through the vertex with the largest value
  int a = (magvert(iv0) > magvert(iv1)) ? iv0 : iv1;
  int b = (magvert(iv2) > magvert(iv3)) ? iv2 : iv3;
  a = (magvert(a) > magvert(b)) ? a : b;

  int flip = (a == iv1 || a == iv3) ? 1 : 0;

  if (level < LIN_MAX_LEVEL)
  {
    int i;
    if (!(level & 1))
    {
      // obtain solution values and physical element coordinates
      xsln->set_quad_order(1, xitem);
      ysln->set_quad_order(1, yitem);
      xval = xsln->get_values(xia, xib);
      yval = ysln->get_values(yia, yib);
      for (i = 0; i < lin_np_quad[1]; i++) {
        double m = getmag(i);
        if (finite(m) && fabs(m) > max) max = fabs(m);
      }

      if (curved)
      {
        RefMap* refmap = xsln->get_refmap();
        phx = refmap->get_phys_x(1);
        phy = refmap->get_phys_y(1);
      }
      idx = quad_indices[0];
    }

    // obtain linearized values and coordinates at the midpoints
    double midval[4][5];
    for (i = 0; i < 4; i++)
    {
      midval[i][0] = (verts[iv0][i] + verts[iv1][i]) * 0.5;
      midval[i][1] = (verts[iv1][i] + verts[iv2][i]) * 0.5;
      midval[i][2] = (verts[iv2][i] + verts[iv3][i]) * 0.5;
      midval[i][3] = (verts[iv3][i] + verts[iv0][i]) * 0.5;
      midval[i][4] = (midval[i][0]  + midval[i][2])  * 0.5;
    };

    // determine whether or not to split the element
    bool split;
    if (eps >= 1.0)
    {
      //if eps > 1, the user wants a fixed number of refinements (no adaptivity)
      split = (level < eps);
    }

    else
    {
      // the value of the middle point is not the average of the four vertex values, since quad == 2 triangles
      midval[2][4] = flip ? (verts[iv0][2] + verts[iv2][2]) * 0.5 : (verts[iv1][2] + verts[iv3][2]) * 0.5;
      midval[3][4] = flip ? (verts[iv0][3] + verts[iv2][3]) * 0.5 : (verts[iv1][3] + verts[iv3][3]) * 0.5;

      // calculate the approximate error of linearizing the normalized solution
      double err = fabs(getmag(idx[0]) - midmag(0)) +
                   fabs(getmag(idx[1]) - midmag(1)) +
                   fabs(getmag(idx[2]) - midmag(2)) +
                   fabs(getmag(idx[3]) - midmag(3)) +
                   fabs(getmag(idx[4]) - midmag(4));
      split = !finite(err) || err > max*4*eps;

      // do the same for the curvature
      if (curved && !split)
      {
        double cerr = 0.0, cden = 0.0; // fixme
        for (i = 0; i < 5; i++)
        {
          cerr += fabs(phx[idx[i]] - midval[0][i]) + fabs(phy[idx[i]] - midval[1][i]);
          cden += fabs(phx[idx[i]]) + fabs(phy[idx[i]]);
        }
        split = (cerr > cden*2.5e-4);
      }

      // do extra tests at level 0, so as not to miss functions with zero error at edge midpoints
      if (level == 0 && !split)
      {
        err = fabs(getmag(13) - 0.5*(midmag(0) + midmag(1))) +
              fabs(getmag(17) - 0.5*(midmag(1) + midmag(2))) +
              fabs(getmag(20) - 0.5*(midmag(2) + midmag(3))) +
              fabs(getmag(9)  - 0.5*(midmag(3) + midmag(0)));
        split = !finite(err) || (err) > max*2*eps; //?
      }
    }

    // split the quad if the error is too large, otherwise produce two linear triangles
    if (split)
    {
      if (curved)
        for (i = 0; i < 5; i++) {
          midval[0][i] = phx[idx[i]];
          midval[1][i] = phy[idx[i]];
        }

      // obtain mid-edge and mid-element vertices
      int mid0 = get_vertex(iv0,  iv1,  midval[0][0], midval[1][0], getvalx(idx[0]), getvaly(idx[0]));
      int mid1 = get_vertex(iv1,  iv2,  midval[0][1], midval[1][1], getvalx(idx[1]), getvaly(idx[1]));
      int mid2 = get_vertex(iv2,  iv3,  midval[0][2], midval[1][2], getvalx(idx[2]), getvaly(idx[2]));
      int mid3 = get_vertex(iv3,  iv0,  midval[0][3], midval[1][3], getvalx(idx[3]), getvaly(idx[3]));
      int mid4 = get_vertex(mid0, mid2, midval[0][4], midval[1][4], getvalx(idx[4]), getvaly(idx[4]));

      // recur to sub-elements
      push_transform(0);  process_quad(iv0, mid0, mid4, mid3, level+1, xval, yval, phx, phy, quad_indices[1]);  pop_transform();
      push_transform(1);  process_quad(mid0, iv1, mid1, mid4, level+1, xval, yval, phx, phy, quad_indices[2]);  pop_transform();
      push_transform(2);  process_quad(mid4, mid1, iv2, mid2, level+1, xval, yval, phx, phy, quad_indices[3]);  pop_transform();
      push_transform(3);  process_quad(mid3, mid4, mid2, iv3, level+1, xval, yval, phx, phy, quad_indices[4]);  pop_transform();
      return;
    }
  }

  // output two linear triangles,
  if (!flip)
  {
    add_triangle(iv3, iv0, iv1);
    add_triangle(iv1, iv2, iv3);
  }
  else
  {
    add_triangle(iv0, iv1, iv2);
    add_triangle(iv2, iv3, iv0);
  }
}