acm模板-三维计算几何

发布于 2022-04-14  2290 次阅读


三维计算几何

const double eps = 1e-8;
const double pi = M_PI;
const int N = 2010;

double abss(double p) { return p < 0 ? -p : p; }

bool equal(double p, double q) { return abss(p - q) < eps; }

double cos(double a, double b, double c) {
  return (a * a + b * b - c * c) / (2 * a * b);
}  //余弦公式

struct point {
  double x, y, z;

  point() { x = y = z = 0; }

  point(double xx, double yy, double zz) {
    x = xx;
    y = yy;
    z = zz;
  }

  double length() { return sqrt(x * x + y * y + z * z); }  //向量长度
  point operator+(const point &p) const { return {x + p.x, y + p.y, z + p.z}; }
  point operator-(const point &p) const { return {x - p.x, y - p.y, z - p.z}; }
};

point operator*(double k, point p) {
  return {k * p.x, k * p.y, k * p.z};
}  //数乘
double dot_product(point p, point q) {
  return p.x * q.x + p.y * q.y + p.z * q.z;
}  //点积
point cross_product(point p, point q) {
  return {p.y * q.z - p.z * q.y, p.z * q.x - p.x * q.z, p.x * q.y - p.y * q.x};
}  //叉积
double distance(point p, point q) {
  return sqrt((p.x - q.x) * (p.x - q.x) + (p.y - q.y) * (p.y - q.y) +
              (p.z - q.z) * (p.z - q.z));
}  //两点间距离
struct plane {
  double A, B, C, D;

  plane() { A = B = C = D = 0; }
  plane(double a, double b, double c, double d) { A = a, B = b, C = c, D = d; }
  plane(point p, point q, point s) {
    point v = cross_product(q - p, s - p);
    A = v.x, B = v.y, C = v.z;
    D = -dot_product(v, p);
  }  //三点共面
  plane(plane p, point q) {
    A = p.A, B = p.B, C = p.C;
    D = -dot_product(p.get_normal_vector(), q);
  }  //过q点平行于p的平面
  point get_normal_vector() const { return {A, B, C}; }  //法向量
  bool equal(plane p) {
    double a = p.A / A;
    double b = p.B / B;
    double c = p.C / C;
    double d = p.D / D;
    return ::equal(a, b) && ::equal(a, c) && ::equal(a, d);
  }
};

bool ptoplane(point &p, plane &f) {
  point q(0, 0, -f.D / f.C);
  return dot_product(f.get_normal_vector(), p - q) > eps;
}  //点在面同向
double cos(plane p, plane q) {
  return abss(p.A * q.A + p.B * q.B + p.C * q.C) /
         (p.get_normal_vector().length() * q.get_normal_vector().length());
}  //两平面夹角
double Triangle_area(point p, point q, point s) {
  return cross_product(q - p, s - p).length();
}  // 2倍的三角形面积

double Tetrahedron_volume(point p, point q, point s, point t) {
  return dot_product(cross_product(q - p, s - p), t - p);
}  //四面体有向体积*6
struct line {
  point origin, direction;

  line(point p, point q) {
    direction = q - p;
    origin = p;
  }  //空间中两点确定一条直线(由p到q)
  line(plane p, plane q) {
    origin = point((q.D * p.B - p.D - q.B) / (q.A * p.B - p.A - q.B),
                   (q.D * p.A - p.D * q.A) / (p.A * q.B - p.B * q.A), 0);
    direction = point(p.B * q.C - p.C * q.B, p.C * q.A - p.A * q.C,
                      p.A * q.B - p.B * q.A);
  }  //两个平面的交线
  line(line p, point q) {
    direction = p.direction;
    origin = q;
  }  //过直线p外一点q与已知直线平行
};

struct ball {
  point center;
  double radius;
};

int rela_between_balls(ball p, ball q) {
  if (p.radius < q.radius) swap(p, q);
  double dis = distance(p.center, q.center);
  if (abss(dis - p.radius - q.radius) < eps)
    return -1;  //外切
  else if (abss(dis - p.radius + q.radius) < eps)
    return 1;  //内切
  else if (dis > p.radius + q.radius)
    return -2;  //相离
  else if (dis < p.radius - q.radius)
    return 2;  //包含
  else
    return 0;  //相交
}  //两球之间的关系

double area_of_intersected_ball(ball p, ball q) {
  if (p.radius < q.radius) swap(p, q);
  int s = rela_between_balls(p, q);
  if (!s) {
    double dis = distance(p.center, q.center);
    double h1 = p.radius * (1 - cos(p.radius, dis, q.radius));
    double h2 = q.radius * (1 - cos(q.radius, dis, p.radius));
    return pi / 3 * (3 - p.radius - h1) * h1 * h1 +
           pi / 3 * (3 - q.radius - h2) * h2 * h2;
  } else if (s > 0)
    return 4 * pi * q.radius * q.radius * q.radius / 3;
  else
    return 0;
}  //两球相交部分体积

point an[N];
int num;

struct tri_plane {
  plane P;
  int a, b, c;
  bool ok;

  tri_plane() {
    P = plane(0, 0, 0, 0);
    a = b = c = 0;
    ok = false;
  }

  tri_plane(int A, int B, int C, bool flag) {
    P = plane(an[A], an[B], an[C]);
    a = A, b = B, c = C;
    ok = flag;
  }
};

struct convex {
  int cnt{};
  tri_plane bn[N];
  int vis[N][N]{};

  void dfs(int p, int f) {
    bn[f].ok = false;  //当前面需要删除,因为它在更大的凸包里面
    //下面把边反过来(先b,后a),以便在deal()中判断与当前面(cnt)共边(ab)的那个面。即判断与当头面(cnt)相邻的3个面(它们与当前面的共边是反向的,如下图中(1)的法线朝外(即逆时针)的面130和312,它们共边13,但一个方向是13,另一个方向是31)
    deal(p, bn[f].b, bn[f].a);
    deal(p, bn[f].c, bn[f].b);
    deal(p, bn[f].a, bn[f].c);
  }

  void deal(int p, int a, int b) {
    int f = vis[a][b];
    if (bn[f].ok) {
      if ((ptoplane(an[p], bn[f].P)))
        dfs(p,
            f);  //如果p点能看到该面f,则继续深度探索f的3条边,以便更新新的凸包面
      else  //否则因为p点只看到cnt面,看不到f面,则p点和a、b点组成一个三角形。
      {
        tri_plane add(b, a, p, true);
        bn[++cnt] = add;
        vis[p][b] = vis[a][p] = vis[b][a] = cnt;
      }
    }
  }

  void construct() {  //构建凸包
    memset(vis, 0, sizeof(vis));
    cnt = 0;
    if (num < 4) return;
    bool tmp = true;
    for (int i = 2; i <= num; i++)  //前两点不共点
      if ((distance(an[1], an[i])) > eps) {
        swap(an[2], an[i]);
        tmp = false;
        break;
      }
    if (tmp) return;
    tmp = true;
    for (int i = 3; i <= num; i++)  //前三点不共线
      if (cross_product((an[1] - an[2]), (an[2] - an[i])).length() > eps) {
        swap(an[3], an[i]);
        tmp = false;
        break;
      }
    if (tmp) return;
    tmp = true;
    for (int i = 4; i <= num; i++)  //前四点不共面
      if (abss(dot_product(cross_product((an[1] - an[2]), (an[2] - an[3])),
                           (an[1] - an[i]))) > eps) {
        swap(an[4], an[i]);
        tmp = false;
        break;
      }
    if (tmp) return;
    for (int i = 1; i <= 4;
         i++)  //构建初始四面体(4个点为origin[1],origin[2],origin[3],origin[4])
    {
      tri_plane add(i % 4 + 1, (i + 1) % 4 + 1, (i + 2) % 4 + 1, true);
      if ((ptoplane(an[i], add.P)) > 0)
        swap(add.b, add.c);  //保证逆时针,即法向量朝外,这样新点才可看到。
      // add.A = as[add.a], add.B = as[add.b], add.C = as[add.c];
      add.P = plane(an[add.a], an[add.b], an[add.c]);
      bn[++cnt] = add;
      vis[add.a][add.b] = vis[add.b][add.c] = vis[add.c][add.a] =
          cnt;  //逆向的有向边保存
    }
    for (int i = 5; i <= num; i++)  //构建更新凸包
      for (int j = 1; j <= cnt;
           j++)  //对每个点判断是否在当前3维凸包内或外(i表示当前点,j表示当前面)
        if (bn[j].ok && (ptoplane(an[i], bn[j].P)) >
                            eps)  //对当前凸包面进行判断,看是否点能否看到这个面
        {
          dfs(i, j);
          break;  //点能看到当前面,更新凸包的面(递归,可能不止更新一个面)。当前点更新完成后break跳出循环
        }
    int tot =
        cnt;  //这些面中有一些tri[i].ok=0,它们属于开始建立但后来因为在更大凸包内故需删除的,所以下面几行代码的作用是只保存最外层的凸包
    cnt = 0;
    for (int i = 1; i <= tot; i++) {
      if (bn[i].ok) bn[++cnt] = bn[i];
    }
  }

  double area()  //表面积
  {
    double ret = 0;
    for (int i = 1; i <= cnt; i++)
      ret += Triangle_area(an[bn[i].a], an[bn[i].b], an[bn[i].c]);
    return ret / 2;
  }

  double volume()  //体积
  {
    point p(0, 0, 0);
    double ret = 0;
    for (int i = 1; i <= cnt; i++)
      ret += Tetrahedron_volume(p, an[bn[i].a], an[bn[i].b], an[bn[i].c]);
    return fabs(ret / 6);
  }

  int facetri() { return cnt; }  //表面三角形数
  int facepolygon()              //表面多边形数
  {
    int ans = 0, i, j, k;
    for (i = 1; i <= cnt; i++) {
      for (j = 1, k = 1; j < i; j++) {
        if (bn[i].P.equal(bn[j].P)) {
          k = 0;
          break;
        }
      }
      ans += k;
    }
    return ans;
  }
} hull;
Hello, world!
最后更新于 2022-04-14