判断点是否在三角形内 - 画三角形的重要方法

判断点是否在三角形内。是画三角形的重要方法,尤其是做游戏的程序员,这种算法值得收纳。
实验环境:Win10
实验工具:CodeBlocks

补充知识

  • 判断点B是否在直线AC上,首先只要满足↑BA×↑BC=0,点B就在直线上,其次如果↑BA·↑BC<0说明B在线段AC内部。如果允许B与端点重合,则只要↑BA·↑BC≤0.
  • 判断点A是否在三角形BCD内部。只要满足↑AB×↑AC,↑AC×↑AD和↑AD×↑AB同号即可,相当于沿顺时针或者逆时针绕三角形一圈时,点A始终出现在同一侧。
  • 点积:↑a·↑b=a1b1+a2b2+a3b3=|↑a|·|↑b|cosx,x为两向量夹角。
    • a·b>0 两向量方向基本相同,夹角在0°到90°之间
    • a·b=0 两向量正交,相互垂直
    • a·b<0 两向量方向基本相反,夹角在90°到180°之间
  • 叉积:↑a×↑b=(a2b3-a3b2)↑i+(a3b1-a1b3)↑j+(a1b2-a2b1)↑k=|↑a|·|↑b|sinx↑n,↑n为↑a×↑b方向。
  • 内容取自《ACM国际大学生程序设计竞赛·知识与入门》P143

面积法

如果点P在三角形ABC内,那么,三角形ABC的面积可以分解为三个小三角形PAB,PBC,PAC的面积之和。
求三角形面积我推荐两种方法:

  • 海伦公式法求面积:S=sqrt(p*(p-a)(p-b)(p-c))
    公式说明:a,b,c分别为三角形三边长,p为半周长,S为三角形的面积。
    如果已知的是三角形abc的坐标就要先取两点距离得边长。
    • 两点之间距离公式:|AB|=sqrt((x1-x2)(x1-x2)-(y1-y2)(y1-y2)),其中A(x1,y1),B(x2,y2).
  • 几何向量积法求面积:我们知道向量积的几何意义就是求得到的平行四边形的面积,而平行四边形的一半就是三角形的面积,通过这个原理就知道求三角形面积的方法。

    • 公式S=(x2-x1)(y3-y2)-(x3-x2)(y2-y1),其中A(x1,y1),B(x2,y2),C(x3,y3).

    注:在求面积的过程中,要注意精度问题。
    以下面积法实验测试用的第二种算面积的公式

    实验效果图

    这里写图片描述

    实验代码
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
float CalSquar(int x1,int y1,int x2,int y2,int x3,int y3)
{
    return fabs(((x2-x1)*(y3-y2)-(y2-y1)*(x3 - x2)))/ 2.0f;
}
bool IsInTri(int x1,int y1,int x2,int y2,int x3,int y3,int x,int y)
{
    float S, S1, S2, S3;
    S = CalSquar(x1,y1,x2,y2,x3,y3);
    S1 = CalSquar(x1,y1,x,y,x2,y2);
    S2 = CalSquar(x2,y2,x,y,x3,y3);
    S3 = CalSquar(x1,y1,x,y,x3,y3);

    float Sum = S1 + S2 + S3;

    if ((-1e-5 < (S - Sum)) && ((S - Sum) < 1e-5))
        return true;
    else return false;
}
int main(){
    int x1=1,y1=1;
    int x2=3,y2=5;
    int x3=5,y3=1;
    int x,y;
    for(int i=0;i<=10;i++){
        for(int j=0;j<=10;j++){
            x=i,y=j;
            if(IsInTri(x1,y1,x2,y2,x3,y3,x,y)){
                printf("x");
            }else printf(" ");
        }
        printf("\n");
    }
    return 0;
}

同向法

这里写图片描述

  • 通过上图,我们知道要想点在三角形内,就要有点P和C在直线AB同侧,直线P和A在直线BC同侧,直线P和B在直线AC同侧。当同时满足时,P在三角形ABC内。
  • 如果判断是否在同侧呢?线性规划知识我们知道。
  • 直线方程为 (y-y1)(x1-x2)-(y1-y2)(x-x1)=0
    • 当 (y-y1)(x1-x2)-(y1-y2)(x-x1)>0时,点(x,y)在直线的右侧
    • 当 (y-y1)(x1-x2)-(y1-y2)(x-x1)<0时,点(x,y)在直线的左侧
  • 通过这个我们就知道如何判断是否同侧了。
实验效果图

这里写图片描述

实验代码

注:d*q要注意精度问题。视情况定,如果最大可能情况d*q>int_max,那么会判错

#include <cstdio>
#include <cstring>
using namespace std;
bool IsInTri(int x1,int y1,int x2,int y2,int x3,int y3,int x,int y)
{
    int d=(y-y1)*(x2-x1)-(y2-y1)*(x-x1);
    int q=(y3-y1)*(x2-x1)-(y2-y1)*(x3-x1);
    if(d*q<0) return false;
    d=(y-y2)*(x3-x2)-(y3-y2)*(x-x2);
    q=(y1-y2)*(x3-x2)-(y3-y2)*(x1-x2);
    if(d*q<0) return false;
    d=(y-y3)*(x1-x3)-(y1-y3)*(x-x3);
    q=(y2-y3)*(x1-x3)-(y1-y3)*(x2-x3);
    if(d*q<0) return false;
    return true;
}
int main(){
    int x1=1,y1=1;
    int x2=2,y2=2;
    int x3=3,y3=1;
    int x,y;
    for(int i=0;i<=10;i++){
        for(int j=0;j<=10;j++){
            x=i,y=j;
            if(IsInTri(x1,y1,x2,y2,x3,y3,x,y)){
                printf("x");
            }else printf(" ");
        }
        printf("\n");
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值