sandbox/Tianyang/embedded_contact_2D/src/geometry_ebm.h

    #define IATOL 1e-10
    
    int facets_ebm (coord n, double alpha, coord p[2])
    {
      int nx = 0;
      int ny = 0;
      coord px[2];
      coord py[2];
    
      for (double s = -0.5; s <= 0.5; s += 1.) {
        if (fabs (n.y) > 0.) {
          double a = (alpha - s*n.x)/n.y;
          if (a >= -0.5 && a <= 0.5) {
            px[nx].x = s;
    	      px[nx++].y = a;
          }
        }
        if (fabs (n.x) > 0.) {
          double a = (alpha - s*n.y)/n.x;
          if (a >= -0.5 && a <= 0.5) {
            py[ny].y = s;
    	      py[ny++].x = a;
          }
        }
      }
    
      if (nx == 2) {
        foreach_dimension() {
          p[0].x = px[0].x;
          p[1].x = px[1].x;
        }
        return 2;
      }
      else if (ny == 2) {
        foreach_dimension() {
          p[0].x = py[0].x;
          p[1].x = py[1].x;
        }
        return 2;
      }
      else {
        int i = 0;
        if (nx > 0) {
          assert (nx == 1);
          p[i].x = px[0].x;
          p[i].y = px[0].y;
          i++;
        }
        if (ny > 0) {
          assert (ny == 1);
          p[i].x = py[0].x;
          p[i].y = py[0].y;
          i++;
        }
        assert (i <= 2);
        return i;
      }
    }
    
    #if dimension == 2
    
    double area_line_triangle (coord p[3], coord n, double alpha)
    {
      assert (n.x != 0. || n.y != 0.);
      assert (p[1].x == 0.5 && p[1].y == 0.5); 
      assert (p[0].y == 0.5);
      assert (p[2].x == 0.5);
    
      double area = 0.5*(0.5 - p[0].x)*(0.5 - p[2].y);
      double d0 = n.x*p[0].x + n.y*p[0].y - alpha;
      double d1 = n.x*p[1].x + n.y*p[1].y - alpha;
      double d2 = n.x*p[2].x + n.y*p[2].y - alpha;
    
      if (d0 >= 0. && d1 >= 0. && d2 >= 0.)
        return 0.;
      else if (d0 <= 0. && d1 <= 0. && d2 <= 0.)
        return area;
      else if (d0 == 0. || d1 == 0. || d2 == 0.) {
        if (d0 == 0.) {
          assert (d1*d2 < 0.);
          assert (fabs(n.y) > 0.);
          double y0 = (alpha - 0.5*n.x)/n.y;
          assert (y0 > p[2].y && y0 < 0.5);
    
          return (d2 > 0. ? 0.5 - y0 : y0 - p[2].y)*(0.5 - p[0].x)/2.;
        }
        else if (d2 == 0.) {
          assert (d0*d1 < 0.);
          assert (fabs(n.x) > 0.);
          double x0 = (alpha - 0.5*n.y)/n.x;
          assert (x0 > p[0].x && x0 < 0.5);
    
          return (d0 > 0. ? 0.5 - x0 : x0 - p[0].x)*(0.5 - p[2].y)/2.;
        }
        else { //d1 == 0
          assert (d0*d2 < 0.);
          assert (fabs((p[2].x - p[0].x)*n.x + (p[2].y - p[0].y)*n.y) > 0.);
          double x0 = ((p[2].x - p[0].x)*alpha - n.y*(p[2].x*p[0].y - p[0].x*p[2].y))/((p[2].x - p[0].x)*n.x + (p[2].y - p[0].y)*n.y);
          assert (x0 > p[0].x && x0 < 0.5);
    
          return (d0 > 0. ? 0.5 - x0 : x0 - p[0].x)*(0.5 - p[2].y)/2.;
        }  
      }
      else {
        assert ((d0 != 0. && d1 != 0. && d2 != 0.));
        if (d0*d1 < 0. && d1*d2 < 0.) {
          assert (fabs(n.x) > 0 && fabs(n.y) > 0.);
          double x0 = (alpha - 0.5*n.y)/n.x;
          double y0 = (alpha - 0.5*n.x)/n.y;
    
          return (d1 < 0. ? (0.5 - x0)*(0.5 - y0)/2. : area - (0.5 - x0)*(0.5 - y0)/2.);
        }
        else if (d1*d2 < 0. && d2*d0 < 0.) {
          assert (fabs(n.y) > 0.);
          double y0 = (alpha - 0.5*n.x)/n.y;
          assert (fabs((p[2].x - p[0].x)*n.x + (p[2].y - p[0].y)*n.y) > 0.);
          double x0 = ((p[2].x - p[0].x)*alpha - n.y*(p[2].x*p[0].y - p[0].x*p[2].y))/((p[2].x - p[0].x)*n.x + (p[2].y - p[0].y)*n.y);
    
          return (d2 < 0. ? (y0 - p[2].y)*(0.5 - x0)/2. : area - (y0 - p[2].y)*(0.5 - x0)/2.);
        }
        else {
          assert (d2*d0 < 0. && d0*d1 < 0.);
          assert (fabs(n.x) > 0.);
          double x0 = (alpha - 0.5*n.y)/n.x;
          assert (fabs((p[2].x - p[0].x)*n.x + (p[2].y - p[0].y)*n.y) > 0.);
          double y0 = ((p[2].y - p[0].y)*alpha + n.x*(p[2].x*p[0].y - p[0].x*p[2].y))/((p[2].x - p[0].x)*n.x + (p[2].y - p[0].y)*n.y);
    
          return (d0 < 0. ? (x0 - p[0].x)*(0.5 - y0)/2. : area - (x0 - p[0].x)*(0.5 - y0)/2.);
        }
      }
    }
    
    double area_line_rectangle (coord p[4], coord n, double alpha)
    {
      assert (p[2].x == 0.5 && p[2].y == 0.5);
    
      double area;
      if (p[1].y == 0.5) {
        assert (p[0].y >= p[3].y);
        assert (p[1].x == -0.5 && p[1].y == 0.5);
        assert (p[0].x == -0.5);
        assert (p[3].x ==  0.5);
    
        area = rectangle_fraction (n, alpha, p[0], p[2])*(p[1].y - p[0].y);
    
        if (p[0].y > p[3].y) {
          coord ptri[3];
          ptri[0] = (coord){-0.5, 0.5};
          ptri[1] = (coord){ 0.5, 0.5};
          ptri[2] = (coord){ 0.5, p[3].y - p[0].y + 0.5};
          alpha += (0.5 - p[0].y)*n.y;
    
          area += area_line_triangle (ptri, n, alpha);
        }
      }
      else {
        assert (p[0].x >= p[3].x);
        assert (p[1].x == 0.5 && p[1].y == -0.5);
        assert (p[3].y ==  0.5);
        assert (p[0].y == -0.5);
    
        area = rectangle_fraction (n, alpha, p[0], p[2])*(p[1].x - p[0].x);
    
        if (p[0].x > p[3].x) {
          coord ptri[3];
          ptri[0] = (coord){p[3].x - p[0].x + 0.5, 0.5};
          ptri[1] = (coord){0.5,  0.5};
          ptri[2] = (coord){0.5, -0.5};
          alpha += (0.5 - p[0].x)*n.x;
    
          area += area_line_triangle (ptri, n, alpha);
        }
      }
    
      return area;
    }
    
    double area_line_pentagon (coord p[5], coord n, double alpha)
    {
      assert (p[1].x == -0.5 && p[1].y ==  0.5);
      assert (p[2].x ==  0.5 && p[2].y ==  0.5);
      assert (p[3].x ==  0.5 && p[3].y == -0.5);
      assert (p[0].x == -0.5 && p[4].y == -0.5);
    
      double area = plane_volume (n, alpha);
    
      foreach_dimension()
        n.x = - n.x;
    
      coord ptri[3];
      foreach_dimension() {
        ptri[0].x = - p[4].x;
        ptri[2].x = - p[0].x;
      }
      ptri[1] = (coord){0.5, 0.5};
      
      area -= area_line_triangle (ptri, n, alpha);
    
      return area;
    }
    
    double line_alpha_ebm (double c, double cs, coord ncs, double alphacs, coord n)
    {
      assert (c > 0 && c < cs && cs < 1.);
    
      foreach_dimension()
        if (ncs.x > 0.) {
          ncs.x = - ncs.x;
          n.x = - n.x;
        }
    
      coord pcs[2];
      assert (facets_ebm (ncs, alphacs, pcs) == 2);
      if (pcs[0].x > pcs[1].x)
        swap (coord, pcs[0], pcs[1]);
      assert (pcs[0].x <= pcs[1].x);
    
      int vnum = 0;
      for (double sx = -0.5; sx <= 0.5; sx += 1.)
        for (double sy = -0.5; sy <= 0.5; sy += 1.)
          if (ncs.x*sx + ncs.y*sy - alphacs < 0.)
            vnum++;
      assert (vnum > 0 && vnum < 4);
      assert (ncs.x*0.5 + ncs.y*0.5 - alphacs < 0.);
    
      coord polygon[5];
      double (* area_line_polygon) (coord *, coord, double);
      if (vnum == 1) {
        polygon[0] = pcs[0];
        polygon[1] = (coord){0.5, 0.5};
        polygon[2] = pcs[1];
    
        area_line_polygon = area_line_triangle;
      }
      else if (vnum == 2) {
        if (ncs.x > ncs.y) {
          assert (ncs.x*(-0.5) + ncs.y*0.5 - alphacs < 0.);
          polygon[0] = pcs[0];
          polygon[1] = (coord){-0.5, 0.5};
          polygon[2] = (coord){ 0.5, 0.5};
          polygon[3] = pcs[1];
        }
        else {
          assert (ncs.x < ncs.y);
          assert (ncs.x*0.5 + ncs.y*(-0.5) - alphacs < 0.);
          polygon[0] = pcs[1];
          polygon[1] = (coord){0.5, -0.5};
          polygon[2] = (coord){0.5,  0.5};
          polygon[3] = pcs[0];
          if (polygon[0].y > polygon[3].y)
            swap (coord, polygon[0], polygon[3]);
        }
    
        area_line_polygon = area_line_rectangle;
      }
      else { //vnum ==3
        assert (ncs.x*(-0.5) + ncs.y*0.5 - alphacs < 0.);
        assert (ncs.x*0.5 + ncs.y*(-0.5) - alphacs < 0.);
    
        polygon[0] = pcs[0];
        polygon[1] = (coord){-0.5,  0.5};
        polygon[2] = (coord){ 0.5,  0.5};
        polygon[3] = (coord){ 0.5, -0.5};
        polygon[4] = pcs[1];
        
        area_line_polygon = area_line_pentagon;
      }
    
      double alpha_polygon[5];
      for (int k = 0; k < vnum+2; k++)
        alpha_polygon[k] = n.x*polygon[k].x + n.y*polygon[k].y;
      
      double alpha_min = nodata;
      for (int k = 0; k < vnum+2; k++)
        if (area_line_polygon (polygon, n, alpha_polygon[k]) < c) {
          alpha_min = alpha_polygon[k];
          break;
        }
    
      double alpha_max = nodata;
      for (int k = 0; k < vnum+2; k++)
        if (area_line_polygon (polygon, n, alpha_polygon[k]) > c) {
          alpha_max = alpha_polygon[k];
          break;
        }
    
      assert (alpha_min != nodata && alpha_max != nodata);
    
      int iternum = 0;
      double alpha_iter = 0.5*(alpha_min + alpha_max);
      double area = area_line_polygon (polygon, n, alpha_iter);
      while (fabs (area - c) > IATOL && iternum <= 100) {
        if (area > c)
          alpha_max = alpha_iter;
        else
          alpha_min = alpha_iter;
    
        alpha_iter = 0.5*(alpha_min + alpha_max);
        area = area_line_polygon (polygon, n, alpha_iter);
        iternum++;
      }
      
      if (iternum > 100)
        fprintf (stdout, "warning: iternum > 100\n");
    
      if (fabs (area - c) <= IATOL)
        return alpha_iter;
      else
        return nodata;
    }
    
    double line_area_ebm (coord pcs[2], double cs, coord ncs, double alphacs, coord n, double alpha)
    {
      assert (cs > 0. && cs < 1.);
    
      foreach_dimension()
        if (ncs.x > 0.) {
          ncs.x = - ncs.x;
          n.x = - n.x;
    
          pcs[0].x = - pcs[0].x;
          pcs[1].x = - pcs[1].x;
        }
    
      if (pcs[0].x > pcs[1].x)
        swap (coord, pcs[0], pcs[1]);
      assert (pcs[0].x <= pcs[1].x);
    
      int vnum = 0;
      for (double sx = -0.5; sx <= 0.5; sx += 1.)
        for (double sy = -0.5; sy <= 0.5; sy += 1.)
          if (ncs.x*sx + ncs.y*sy - alphacs < 0.)
            vnum++;
      assert (vnum > 0 && vnum < 4);
      assert (ncs.x*0.5 + ncs.y*0.5 - alphacs < 0.);
    
      coord polygon[5];
      double (* area_line_polygon) (coord *, coord, double);
      if (vnum == 1) {
        polygon[0] = pcs[0];
        polygon[1] = (coord){0.5, 0.5};
        polygon[2] = pcs[1];
    
        area_line_polygon = area_line_triangle;
      }
      else if (vnum == 2) {
        if (ncs.x > ncs.y) {
          assert (ncs.x*(-0.5) + ncs.y*0.5 - alphacs < 0.);
          polygon[0] = pcs[0];
          polygon[1] = (coord){-0.5, 0.5};
          polygon[2] = (coord){ 0.5, 0.5};
          polygon[3] = pcs[1];
        }
        else {
          assert (ncs.x < ncs.y);
          assert (ncs.x*0.5 + ncs.y*(-0.5) - alphacs < 0.);
          polygon[0] = pcs[1];
          polygon[1] = (coord){0.5, -0.5};
          polygon[2] = (coord){0.5,  0.5};
          polygon[3] = pcs[0];
          if (polygon[0].y > polygon[3].y)
            swap (coord, polygon[0], polygon[3]);
        }
    
        area_line_polygon = area_line_rectangle;
      }
      else { //vnum ==3
        assert (ncs.x*(-0.5) + ncs.y*0.5 - alphacs < 0.);
        assert (ncs.x*0.5 + ncs.y*(-0.5) - alphacs < 0.);
    
        polygon[0] = pcs[0];
        polygon[1] = (coord){-0.5,  0.5};
        polygon[2] = (coord){ 0.5,  0.5};
        polygon[3] = (coord){ 0.5, -0.5};
        polygon[4] = pcs[1];
        
        area_line_polygon = area_line_pentagon;
      }
    
      return area_line_polygon (polygon, n, alpha);
    }
    
    double rectangle_fraction_cs (double cs, double fs, coord ncs, double alphacs, coord n, double alpha, coord a, coord b)
    {
      coord n1, ncs1;
      foreach_dimension() {
        alpha -= n.x*(b.x + a.x)/2.;
        n1.x = n.x*(b.x - a.x);
    
        alphacs -= ncs.x*(b.x + a.x)/2.;
        ncs1.x = ncs.x*(b.x - a.x);
      }
    
      coord pcs[2];
      int num = facets_ebm (ncs1, alphacs, pcs);
    
      if (num < 2)
        return plane_volume (n1, alpha);
    
      return line_area_ebm (pcs, cs, ncs1, alphacs, n1, alpha);
    }
    
    #endif