三维几何之判断俩个一个六面题的重心稳定性

本文详细阐述了一种通过几何原理解决复杂压纸器稳定性问题的算法,包括顶点枚举、底面选择、重心计算、距离判断等关键步骤,确保压纸器稳定放置。

分析:

压纸器一共有5个顶点,底面至少有三个顶点,所以可以枚举底面上的三个顶点P1P2P3,判断是否可以以该面为底进行稳定放置。具体来说需要判断是否所有顶点都在该面的

同侧,然后整个压纸器的重心是否在底面三角形的内部,且离各边的距离不超过0.2.

有一种特殊情况是很容易漏掉:如果还有一个点P4也在底面上(即四点共面),那么重心落在P1P2P3P4的凸包内即可,不一定在三角形P1P2P3内。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#define LL long long
using namespace std;
const double eps=1e-8;
int dcmp(double x)
{
    if(fabs(x)<eps)
        return 0;
    else
        return x>0?1:-1;
}
struct Point3
{
    double x,y,z;
    Point3(double x=0,double y=0,double z=0):x(x),y(y),z(z) {}
};
typedef Point3 Vector3;
Vector3 operator + (const Vector3 &A,const Vector3 &B)
{
    return Vector3(A.x+B.x,A.y+B.y,A.z+B.z);
}
Vector3 operator - (const Point3 &A,const Point3 &B)
{
    return Vector3(A.x-B.x,A.y-B.y,A.z-B.z);
}
Vector3 operator *(const Vector3 &A, double p)
{
    return Vector3(A.x*p,A.y*p,A.z*p);
}
Vector3 operator /(const Vector3 &A,double p)
{
    return Vector3(A.x/p,A.y/p,A.z/p);
}
double Dot(const Vector3 &A,const Vector3 &B) {return A.x*B.x+A.y*B.y+A.z*B.z;}
double Length(const Vector3 &A){return sqrt(Dot(A,A));}
double Angle(const Vector3 &A,const Vector3 &B){return acos(Dot(A,B)/Length(A)/Length(B));}
Vector3 Cross(const Vector3 &A,const Vector3 &B)
{
    return Vector3(A.y*B.z-A.z*B.y,A.z*B.x-A.x*B.z,A.x*B.y-A.y*B.x);
}
double Area2(const Point3 &A,const Point3 &B,const Point3 &C)
{
    return Length(Cross(B-A,C-A));
}
double Volume6(const Point3 &A,const Point3 &B,const Point3 &C,const Point3 &D)
{
    return Dot(D-A,Cross(B-A,C-A));
}
bool read_point3(Point3 &p)
{
    if(scanf("%lf%lf%lf",&p.x,&p.y,&p.z)!=3) return false;
    return true;
}
double DistaceToPlane(const Point3 &p,const Point3 &p0,const Vector3 &n)
{
    return fabs(Dot(p-p0,n));
}

Point3 GetPlaneProjection(const Point3 &p,const Point3 &p0,const Vector3 &n)
{
    return p-n*Dot(p-p0,n);
}
double DistaceToLine(const Point3 &P,const Point3 &A,const Point3 &B)
{
    Vector3 v1=B-A,v2=P-A;
    return Length(Cross(v1,v2))/Length(v1);
}
bool SameSide(const Point3 &p1,const Point3 &p2,const Point3 &a,const Point3 &b)
{
    return dcmp(Dot(Cross(b-a,p1-a),Cross(b-a,p2-a)))>=0;
}
bool PointInTri(const Point3 &P,const Point3 &P0,const Point3 &P1,const Point3 &P2)
{
    return SameSide(P,P0,P1,P2)&&SameSide(P,P1,P0,P2)&&SameSide(P,P2,P0,P1);
}
Point3  Centroid(const Point3 &A,const Point3 &B,const Point3 &C,const Point3 &D)
{
    return (A+B+C+D)/4.0;
}
bool InsideWithMinDistace(const Point3 &P,const Point3 &A,const Point3 &B,const Point3 &C,double midist)
{
    if(!PointInTri(P,A,B,C)) return false;
    if(DistaceToLine(P,A,B)<midist) return false;
    if(DistaceToLine(P,B,C)<midist) return  false;
    if(DistaceToLine(P,C,A)<midist) return false;
    return true;
}
bool InsideWithMinDistace(const Point3 &P,const Point3 &A,const Point3 &B,const Point3 &C,const Point3 &D,double midist)
{
    if(!PointInTri(P,A,B,C)) return false;
    if(!PointInTri(P,C,D,A)) return false;
    if(DistaceToLine(P,A,B)<midist) return false;
    if(DistaceToLine(P,B,C)<midist) return false;
    if(DistaceToLine(P,C,D)<midist) return false;
    if(DistaceToLine(P,D,A)<midist) return false;
    return true;
}
int main()
{
    for(int kase=1;;kase++)
    {
        Point3 P[5],F;
        for(int i=0;i<5;i++)
            if(!read_point3(P[i])) return 0;
        read_point3(F);
        Point3 c1=Centroid(P[0],P[1],P[2],P[3]);
        Point3 c2=Centroid(P[0],P[1],P[2],P[4]);
        double vol1 =fabs(Volume6(P[0],P[1],P[2],P[3]))/6.0;
        double vol2 = fabs(Volume6(P[0],P[1],P[2],P[4]))/6.0;
        Point3 centroid = (c1*vol1+c2*vol2)/(vol1+vol2);

        double mindist=1e9,maxdist=-1e9;
        for(int i=0;i<5;i++)
            for(int j=i+1;j<5;j++)
                for(int k=j+1;k<5;k++)
        {
            int vis[5]={0};
            vis[i]=vis[j]=vis[k]=1;
            int a,b;
            for(a=0;a<5;a++)
            if(!vis[a]) {b=10-i-j-k-a;break;}

            int d1=dcmp(Volume6(P[i],P[j],P[k],P[a]));
            int d2=dcmp(Volume6(P[i],P[j],P[k],P[b]));
            if(d1*d2<0) continue;

            Vector3 n=Cross(P[j]-P[i],P[k]-P[i]);
            n=n/Length(n);

            Point3 proj=GetPlaneProjection(centroid,P[i],n);
            bool ok= InsideWithMinDistace(proj,P[i],P[j],P[k],0.2);
            if(!ok)
            {
                if(d1==0)
                {
                    if(!InsideWithMinDistace(proj,P[i],P[k],P[j],P[a],0.2)) continue;
                }
                else if(d2==0)
                {
                    if(!InsideWithMinDistace(proj,P[i],P[k],P[j],P[b],0.2)) continue;
                }
                else
                    continue;
            }
            double dist=DistaceToPlane(F,P[i],n);
            mindist=min(mindist,dist);
            maxdist=max(maxdist,dist);
        }
        printf("Case %d: %.5f %.5f\n",kase,mindist,maxdist);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值