• UVA12305 Polishing a Extruded Polygon 多面体切割 [Rujia Liu's Presents, Dedicated to Geometry and CG Lovers]


    不知道这算不算个神题了,AC的时候只有我和汝哥两人过这题……

    想了一天,调了一天,竟然因为三角剖分时就差了一句叉积判断相邻边的凹凸性WA了好几天……

    1、三角剖分:

    所给多面体是非凸的,难以处理,剖分成一个个三棱柱就都是凸多面体了。最后才开始写陌生的三角剖分的,已经写+调了二百多行疲惫不堪的时候看到一计算几何书上好复杂的nlogn算法,竟然还要再搞平衡树,简直要崩溃。百度、谷歌都不给力了,期刊论文看着也晕乎,只好凭着自己的理解来暴力剖分了。

    读入每个点建立双向链表(单向应该也没关系,灵活性差点),然后开始循环剖分,对每个相邻边,先判断凹凸性,然后枚举其他所有边判是否和如图所示虚线交叉,通过判断后可以确定这是个凸出的角,可以切掉,就得到了一个三角形。不停地对剩下的多边形进行这样的操作,每一时刻一定会有这样的凸出的角可以切,最后可以完成剖分。

    2、模拟切割:

    剖分的同时就建立了一个个三棱柱,对“外侧”的面打上标记,这样才能计算正确的表面积。我用链表来存多面体的各个面,用构成多面体的各个面来表示这个多面体。切割的时候就是切这些面,思考起来挺麻烦的,实现的时候思路还是比较清晰,切割凸多面体得到的任何面一定还是凸多边形,而切凸多边形只需要把切面一侧的点去掉,与切面相交的两点连起来,和剩下的点围成新的凸包。对于切掉的整个面,乃至整个凸多面体,就可以有效地利用链表轻松删除。

    3、计算体积和面积:

    面积就用多边形面积公式,和二维坐标计算的原理是一样的。由于是凸多面体,那么凸多面体内任选一点,就可以把多面体分割成一个个棱锥,棱锥的体积就是点到面的距离乘以面积除以3。这个点可以随意取,取多面体的一个顶点就非常方便了。

    调试虽然花了不少时间,但是工作量并不算大,基本上调出样例就差不多了。

    应该是没有三点共线的数据,我把处理这个的代码删了是可以AC的。

    好长的代码……

    View Code
      1 #include<stdio.h>
      2 #include<string.h>
      3 #include<stdlib.h>
      4 #include<math.h>
      5 #include<vector>
      6 #include<list>
      7 #include<algorithm>
      8 #include<iostream>
      9 using namespace std;
     10 const int maxn = 3011;
     11 const double eps = 1e-8;
     12 inline int dcmp(double x)
     13 {
     14     return x > eps ? 1 : (x < -eps ? -1 : 0);
     15 }
     16 inline double pz(double x)
     17 {
     18     return dcmp(x) ? x : 0;
     19 }
     20 /******************************************************************************/
     21 //定义三维点
     22 struct Point3
     23 {
     24     double x, y, z;
     25     Point3()
     26     {
     27         x = y = z = 0;
     28     }
     29     Point3(double a, double b, double c)
     30     {
     31         x = a, y = b, z = c;
     32     }
     33     Point3 cross(Point3 p)
     34     {
     35         return Point3(y * p.z - p.y * z,
     36                       z * p.x - x * p.z,
     37                       x * p.y - y * p.x);
     38     }
     39     double dot(Point3 p)
     40     {
     41         return x * p.x + y * p.y + z * p.z;
     42     }
     43     Point3 operator-(const Point3 &p)const
     44     {
     45         return Point3(x - p.x, y - p.y, z - p.z);
     46     }
     47     Point3 operator-()const
     48     {
     49         return Point3(-x, -y, -z);
     50     }
     51     Point3 operator+(const Point3 &p)const
     52     {
     53         return Point3(x + p.x, y + p.y, z + p.z);
     54     }
     55     Point3 operator*(const double b)const
     56     {
     57         return Point3(x * b, y * b, z * b);
     58     }
     59     bool operator==(const Point3 &p)const
     60     {
     61         return !dcmp(x - p.x) && !dcmp(y - p.y) && !dcmp(z - p.z);
     62     }
     63     bool operator!=(const Point3 &p)const
     64     {
     65         return !(*this == p);
     66     }
     67     bool operator<(const Point3 &p)const
     68     {
     69         if(!dcmp(x - p.x))
     70         {
     71             if(!dcmp(y - p.y))
     72                 return dcmp(z - p.z) < 0;
     73             return dcmp(y - p.y) < 0;
     74         }
     75         return dcmp(x - p.x) < 0;
     76     }
     77     bool operator>(const Point3 &p)const
     78     {
     79         return p < *this;
     80     }
     81     bool operator>=(const Point3 &p)const
     82     {
     83         return !(*this < p);
     84     }
     85     bool operator<=(const Point3 &p)const
     86     {
     87         return !(*this > p);
     88     }
     89     Point3 fxl(Point3 b, Point3 c)//平面法向量
     90     {
     91         return (*this - b).cross(b - c);
     92     }
     93     double Dis(Point3 b)
     94     {
     95         return sqrt((*this - b).dot(*this - b));
     96     }
     97     double vlen()
     98     {
     99         return sqrt(dot(*this));
    100     }
    101     bool PinLine(Point3 b, Point3 c)//三点共线
    102     {
    103         return fxl(b, c).vlen() < eps;
    104     }
    105     bool PonPlane(Point3 b, Point3 c, Point3 d)//四点共面
    106     {
    107         return !dcmp(fxl(b, c).dot(d - *this));
    108     }
    109     bool PonSeg(Point3 b, Point3 c)//点在线段上,包括端点
    110     {
    111         return !dcmp((*this - b).cross(*this - c).vlen()) &&
    112                (*this - b).dot(*this - c) <= 0;
    113     }
    114     bool PinSeg(Point3 b, Point3 c)//点在线段上,不包括端点
    115     {
    116         return PonSeg(b, c) && *this != b && *this != c;
    117     }
    118     double PtoLine(Point3 b, Point3 c)//点到直线距离
    119     {
    120         return (*this - b).cross(c - b).vlen() / b.Dis(c);
    121     }
    122     double PtoPlane(Point3 b, Point3 c, Point3 d)//点到平面距离
    123     {
    124         return fabs(b.fxl(c, d).dot(*this - b)) / b.fxl(c, d).vlen();
    125     }
    126 };
    127 /******************************************************************************/
    128 //定义平面+空间平面凸包
    129 struct Plane
    130 {
    131     double a, b, c, d;
    132     bool outplane;//计入表面积的面
    133     vector<Point3> p;
    134     Plane()
    135     {
    136         a = b = c = d = 0, outplane = false;
    137         p.clear();
    138     }
    139     inline void init(double a_, double b_, double c_, double d_)
    140     {
    141         a = a_, b = b_, c = c_, d = d_;
    142         p.clear();
    143     }
    144     inline void init(Point3 pa, Point3 pb, Point3 pc)
    145     {
    146         Point3 t = (pa - pb).cross(pa - pc);
    147         a = t.x, b = t.y, c = t.z;
    148         d = -(pa.x * t.x + pa.y * t.y + pa.z * t.z);
    149         p.clear();
    150     }
    151     Plane(double a_, double b_, double c_, double d_)
    152     {
    153         init(a_, b_, c_, d_);
    154     }
    155     Plane(Point3 pa, Point3 pb, Point3 pc)
    156     {
    157         init(pa, pb, pc);
    158     }
    159     double PtoPlane(const Point3 &pa)const//点面距离
    160     {
    161         return fabs(Sub(pa)) / sqrt(a * a + b * b + c * c);
    162     }
    163     double Sub(const Point3 &p)const//点代入方程
    164     {
    165         return p.x * a + p.y * b + p.z * c + d;
    166     }
    167     Point3 PcrossPlane(Point3 a, Point3 b)
    168     {
    169         return a + (b - a) * (PtoPlane(a) / (PtoPlane(a) + PtoPlane(b)));
    170     }
    171     int Parallel(Plane pl)//面平行
    172     {
    173         if(!dcmp(a * pl.b - b * pl.a) && !dcmp(a * pl.c - c * pl.a) && !dcmp(b * pl.c - c * pl.b))
    174             return dcmp(pl.Sub(p[0])) > 0 ? 1 : -1;
    175         return 0;
    176     }
    177     bool Cut(Plane &pl)//平面切割
    178     {
    179         switch(Parallel(pl))
    180         {
    181         case -1:
    182             return true;
    183         case 1:
    184             return false;
    185         }
    186         int i, j, k, n = p.size();
    187         bool flag1, flag2;
    188         Point3 p1, p2;
    189         vector<Point3> ret;
    190         for(i = flag1 = flag2 = 0; i < n; ++ i)
    191         {
    192             flag1 |= pl.Sub(p[i]) < 0;
    193             flag2 |= pl.Sub(p[i]) > 0;
    194         }
    195         if(flag1 != flag2) return flag1;
    196         if(!flag1) return true;
    197         for(i = 0; pl.Sub(p[i]) >= 0; ++ i);
    198         for(; pl.Sub(p[i]) < 0; i = (i + 1) % n);
    199         for(j = i; pl.Sub(p[j]) >= 0; j = (j + 1) % n);
    200         for(k = j; k != i; k = (k + 1) % n)
    201             ret.push_back(p[k]);
    202         p1 = pl.PcrossPlane(p[i], p[(i + n - 1) % n]);
    203         p2 = pl.PcrossPlane(p[j], p[(j + n - 1) % n]);
    204         ret.push_back(p1), ret.push_back(p2);
    205         p = ret;
    206         pl.p.push_back(p1), pl.p.push_back(p2);
    207         return true;
    208     }
    209     void Graham()//求空间平面凸包
    210     {
    211         int len, i, n, top = 1;
    212         vector<Point3> res;
    213         Point3 fx(a, b, c);
    214         sort(p.begin(), p.end());
    215         vector<Point3>::iterator iter = unique(p.begin(), p.end());
    216         p.erase(iter, p.end());
    217         n = p.size();
    218         res.push_back(p[0]), res.push_back(p[1]);
    219         for(i = 2; i < n; ++ i)
    220         {
    221             while(top && dcmp((res[top] - res[top - 1]).cross(p[i] - res[top]).dot(fx)) <= 0)
    222                 res.pop_back(), -- top;
    223             res.push_back(p[i]), ++ top;
    224         }
    225         len = top;
    226         res.push_back(p[n - 2]), ++ top;
    227         for(i = n - 3; i >= 0; -- i)
    228         {
    229             while(top != len && dcmp((res[top] - res[top - 1]).cross(p[i] - res[top]).dot(fx)) <= 0)
    230                 res.pop_back(), -- top;
    231             res.push_back(p[i]), ++ top;
    232         }
    233         res.pop_back();
    234         p = res;
    235     }
    236     double PolygonArea()//空间平面凸包面积
    237     {
    238         int n = p.size();
    239         double ret = 0;
    240         for(int i = 2; i < n; ++ i)
    241             ret += (p[i - 1] - p[0]).cross(p[i] - p[0]).vlen();
    242         return ret * 0.5;
    243     }
    244 };
    245 /******************************************************************************/
    246 //定义变量
    247 struct Polyhedron//立方体面集合
    248 {
    249     list<Plane> pl;
    250 };
    251 list<Polyhedron> pol;
    252 int n, h, m;//点个数,高度,切割次数
    253 struct PointChain
    254 {
    255     Point3 p;
    256     int pre, nex;
    257     bool outside;//标记发向nex的边为外部边,抬起的面计入总面积
    258 } PC[maxn];
    259 /******************************************************************************/
    260 //判相交,辅助判断三角剖分合法性
    261 inline double det(double x1, double y1, double x2, double y2)
    262 {
    263     return x1 * y2 - x2 * y1;
    264 }
    265 inline double cross2(Point3 a, Point3 b, Point3 c)//平面叉积
    266 {
    267     return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y);
    268 }
    269 inline bool SegCross(Point3 u1, Point3 u2, Point3 v1, Point3 v2)
    270 {
    271     return ((dcmp(cross2(u1, u2, v1)) ^ dcmp(cross2(u1, u2, v2))) == -2 &&
    272             (dcmp(cross2(v1, v2, u1)) ^ dcmp(cross2(v1, v2, u2))) == -2) ||
    273            ((v1.PinSeg(u1, u2) || v2.PinSeg(u1, u2)) &&
    274             (u1.PonSeg(v1, v2) || u2.PonSeg(v1, v2))) ||
    275            ((v1.PonSeg(u1, u2) || v2.PonSeg(u1, u2)) &&
    276             (u1.PinSeg(v1, v2) || u2.PinSeg(v1, v2)));
    277 }
    278 /******************************************************************************/
    279 bool CanDo(int s, int e)
    280 {
    281     int tp = PC[s].nex;
    282     while(tp != s)
    283     {
    284         if(SegCross(PC[s].p, PC[e].p, PC[tp].p, PC[PC[tp].pre].p))
    285             return false;
    286         tp = PC[tp].nex;
    287     }
    288     return true;
    289 }
    290 void AddPolyhedron(int A, int B, int C)
    291 {
    292     Polyhedron t;
    293     Plane pl;
    294     Point3 a = PC[A].p, b = PC[B].p, c = PC[C].p;
    295     Point3 a_ = Point3(a.x, a.y, h), b_ = Point3(b.x, b.y, h), c_ = Point3(c.x, c.y, h);
    296     pl.init(a, b, c);
    297     pl.p.push_back(a);
    298     pl.p.push_back(b);
    299     pl.p.push_back(c);
    300     pl.outplane = true;
    301     t.pl.push_front(pl);
    302     pl.init(a_, b_, c_);
    303     pl.p.push_back(a_);
    304     pl.p.push_back(b_);
    305     pl.p.push_back(c_);
    306     t.pl.push_front(pl);
    307     pl.init(a, b, b_);
    308     pl.p.push_back(a);
    309     pl.p.push_back(b);
    310     pl.p.push_back(b_);
    311     pl.p.push_back(a_);
    312     pl.outplane = PC[A].outside, PC[A].outside = false;
    313     t.pl.push_front(pl);
    314     pl.init(b, c, c_);
    315     pl.p.push_back(b);
    316     pl.p.push_back(c);
    317     pl.p.push_back(c_);
    318     pl.p.push_back(b_);
    319     pl.outplane = PC[B].outside, PC[B].outside = false;
    320     t.pl.push_front(pl);
    321     pl.init(c, a, a_);
    322     pl.p.push_back(c);
    323     pl.p.push_back(a);
    324     pl.p.push_back(a_);
    325     pl.p.push_back(c_);
    326     if(PC[C].nex == A && PC[C].outside)
    327         pl.outplane = true, PC[C].outside = false;
    328     else pl.outplane = false;
    329     t.pl.push_front(pl);
    330     pol.push_front(t);
    331 }
    332 void Triangulation()//循环枚举相邻三点划分三角形
    333 {
    334     int tp = 0;
    335     while(PC[PC[tp].nex].nex != tp)
    336     {
    337         while(dcmp(cross2(PC[tp].p, PC[PC[tp].nex].p, PC[PC[PC[tp].nex].nex].p)) <= 0 || 
    338             !CanDo(tp, PC[PC[tp].nex].nex)) tp = PC[tp].nex;
    339         AddPolyhedron(tp, PC[tp].nex, PC[PC[tp].nex].nex);
    340         PC[PC[PC[tp].nex].nex].pre = tp;
    341         PC[tp].nex = PC[PC[tp].nex].nex;
    342     }
    343 }
    344 void init()
    345 {
    346     double x, y;
    347     int i, tp;
    348     pol.clear();
    349     for(i = 0; i < n; ++ i)
    350     {
    351         scanf("%lf%lf", &x, &y);
    352         PC[i].p = Point3(x, y, 0);
    353         PC[i].pre = i - 1;
    354         PC[i].nex = i + 1;
    355         PC[i].outside = true;
    356     }
    357     PC[0].pre = n - 1, PC[n - 1].nex = 0;
    358     Triangulation();
    359 }
    360 /******************************************************************************/
    361 list<Polyhedron>::iterator iti;
    362 list<Plane>::iterator itj;
    363 double CalSumVol()//求总体积
    364 {
    365     double ans = 0;
    366     Point3 tmp;
    367     for(iti = pol.begin(); iti != pol.end(); ++ iti)
    368     {
    369         for(itj = (*iti).pl.begin(), tmp = (*itj).p[0]; itj != (*iti).pl.end(); ++ itj)
    370             ans += (*itj).PolygonArea() * (*itj).PtoPlane(tmp);
    371     }
    372     return ans / 3;
    373 }
    374 double CalSumArea()//求总面积
    375 {
    376     double ans = 0;
    377     for(iti = pol.begin(); iti != pol.end(); ++ iti)
    378         for(itj = (*iti).pl.begin(); itj != (*iti).pl.end(); ++ itj)
    379             if((*itj).outplane) ans += (*itj).PolygonArea();
    380     return ans;
    381 }
    382 
    383 void MakeAns()
    384 {
    385     double a, b, c, d;
    386     bool flag;
    387     while(m --)
    388     {
    389         scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
    390         for(iti = pol.begin(); iti != pol.end();)
    391         {
    392             Plane tmp(a, b, c, d);
    393             tmp.outplane = true;
    394             for(itj = (*iti).pl.begin(), flag = false; itj != (*iti).pl.end();)
    395             {
    396                 if(!(*itj).Cut(tmp))
    397                 {
    398                     list<Plane>::iterator tmpj = itj;
    399                     ++ itj;
    400                     (*iti).pl.erase(tmpj);
    401                 }
    402                 else flag = true, ++ itj;
    403             }
    404             if(!flag)
    405             {
    406                 list<Polyhedron>::iterator tmpi = iti;
    407                 ++ iti;
    408                 pol.erase(tmpi);
    409             }
    410             else if(tmp.p.size())
    411             {
    412                 tmp.Graham();
    413                 (*iti).pl.push_front(tmp);
    414                 ++ iti;
    415             }
    416             else ++ iti;
    417         }
    418         printf("%.3f %.3f\n", CalSumVol(), CalSumArea());
    419     }
    420 }
    421 int main()
    422 {
    423     while(scanf("%d%d%d", &n, &h, &m), n | h | m)
    424     {
    425         init();
    426         printf("%.3f %.3f\n", CalSumVol(), CalSumArea());
    427         MakeAns();
    428     }
    429     return 0;
    430 }
  • 相关阅读:
    Android自动化框架学习中遇到的方法
    Python中使用adb命令行
    monkeyrunner无法运行的问题解决方案总结
    TCP与UDP的区别
    KVM虚拟机的认知
    HTTP状态码分类
    FTP主动模式(Port)和被动模式(Passive)的区别
    Linux df -h 显示磁盘空间满,但实际未占用满——问题分析
    浅谈AD域
    zabbix连接Mysql提示Can’t connect to local MySQL server through socket的解决方法
  • 原文地址:https://www.cnblogs.com/CSGrandeur/p/2681222.html
Copyright © 2020-2023  润新知