【學習筆記】立體計算幾何

【基本知識】

向量運算:
模長:len=x2+y2+z2len=\sqrt{x^2+y^2+z^2}
加減:對應座標加減,結果爲一個向量;
點積:依舊定義爲a\vec{a}b\vec{b}的投影,計算方式爲將對應座標相乘後相加,結果爲一個值。如
(x1,y1,z1)(x2,y2,z2)=x1x2+y1y2+z1z2(x_1,y_1,z_1) \cdot(x_2,y_2,z_2)=x_1x_2+y_1y_2+z_1z_2
叉積:**叉積的結果是一個向量。**方向可用“右手定則”~~(瞎編的名字)~~確定:食指指向一個向量,中指指向另一個,大拇指與中指、食指所在平面垂直。則大拇指的方向即爲叉積結果的方向。
在這裏插入圖片描述
座標計算:
(x1,y1,z1)×(x2,y2,z2)=(y1z2y2z1,z1x2x1z2,x1y2x2y1)(x_1,y_1,z_1) \times (x_2,y_2,z_2)=(y_1z_2-y_2z_1,z_1x_2-x_1z_2,x_1y_2-x_2y_1)
平面的法向量:見向量叉積的定義。


基本操作:
·判斷點D是否在平面ABC內: 求平面ABC的法向量n\vec{n},若nAD=0\vec{n} \cdot \overrightarrow{AD}=0,則DABCD \in 平面ABC
點D到平面ABC距離: 由向量點積的定義:dis=nADndis=\frac{\vec{n} \cdot \overrightarrow{AD}}{|\vec{n}|}


算法(持續更新):
求凸包。
step1:擾動
將每一個點的座標加/減一個足夠小的隨機值,使其不會引起誤差,並能保證不會出現四點共面的情況。
step2:記錄平面
由於最多三點在一個平面上,故通過記錄平面上的三個點記錄一個平面。
step3:構造
假如我們構造出了一個凸包。現在嘗試擴展。
我們從待選點中拿一個出來,記爲PP。從PP向凸包作射線。
不難發現,這若干條射線一定與凸包交於若干個棱上。
在這裏插入圖片描述
將可見面刪去,並將PP加入凸包。這樣,我們就完成了一次擴展。
在這裏插入圖片描述
爲保證正確性,需要對每一個平面判斷是否可見,故時間複雜度爲O(n2)O(n^2)
判斷某個點是否可見:用平面的法向量與P到平面上的任意一點做點積。若結果>0>0(即夾角爲銳角),則該平面可見;反之不可見。

#include<bits/stdc++.h>
using namespace std;
const int mn = 2005;
const double eps = 1e-10;
double Rand() {return rand() / (double) RAND_MAX;}
double reps() {return (Rand() - 0.5) * eps;}
struct point{
    double x, y, z;
    void shake() {x += reps(), y += reps(), z += reps();}
    double len() {return sqrt(x * x + y * y + z * z);}
    point operator -(const point b) const {return (point) {x - b.x, y - b.y, z - b.z};}
    point operator *(const point b) const {return (point) {y * b.z - b.y * z, z * b.x - b.z * x, x * b.y - b.x * y};}
    double operator&(const point b) const {return x * b.x + y * b.y + z * b.z;}
}p[mn];
struct surf{
    int v[3];
    point normal() {return (p[v[1]] - p[v[0]]) * (p[v[2]] - p[v[0]]);}
    double S() {return normal().len() / 2.0;}
}f[mn], c[mn];
int n, cnt, vis[mn][mn];
int check(surf f, point pp) {return ((pp - p[f.v[0]]) & f.normal()) > 0;}
inline void convey()
{
    f[++cnt] = (surf) {1, 2, 3}, f[++cnt] = (surf) {3, 2, 1};
    for(int i = 4, tmp = 0; i <= n; i++)
    {
        for(int j = 1, v; j <= cnt; j++)
        {
            if(!(v = check(f[j], p[i]))) c[++tmp] = f[j];
            for(int k = 0; k < 3; k++) vis[f[j].v[k]][f[j].v[(k + 1) % 3]] = v;
        }
        for(int j = 1; j <= cnt; j++)
            for(int k = 0; k < 3; k++)
            {
                int x = f[j].v[k], y = f[j].v[(k + 1) % 3];
                if(vis[x][y] && !vis[y][x]) c[++tmp] = (surf) {x, y, i};
            }
        for(int j = 1; j <= tmp; j++) f[j] = c[j];
        cnt = tmp, tmp = 0;
    }
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z), p[i].shake();
    convey(); double ans = 0;
    for(int i = 1; i <= cnt; i++) ans += f[i].S();
    printf("%.6f\n", ans);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章