golang 二维平面求多重遮挡三角形总面积

解决问题描述:二维平面有很多三角形错落,可能会相互叠加落在一起,也可能互相远离。目标求出这些三角形的总占地面积。

我最开始想的解决方案是用总面积-总重叠面积 = 总占地面积。后来实现起来发现当面临多次重叠时,这样算是错误的。

后来参考了一些文献,得出结论:这个问题抽象出来就是求n个集合的并集问题。公式如下:

A1∪A2∪......∪An =

A1 + A2 + ......+ An ﹣(A1∩A2 + A1∩A3 + ......+ A[n-1]∩An) +

  (A1∩A2∩A3 + A1∩A2∩A4 + ......+ A[n-2]∩A[n-1]∩An) + ......+

[(-1)^(n-1)]A1∩A2∩......∩An

第二个需要解决的问题是如何判断两个三角形关系并求两个三角形的重叠面积

思路如下:

第一步:两个三角形各边分别求交点,交点放入集合a中

第二步:两个三角形分别求一个点是否在另一个三角形内部,内点放入集合a中

第三步:集合a去重

第四部:如果a中元素数量>=3,则两三角形相交,重叠部分面积为a中点集所构成的凸多边形的面积。如果a中元素数量<3,则两三角形不相交。

这两个问题解决,问题就迎刃而解了,中间还涉及了很多小函数,整体代码如下:

type Vector2 struct {
    X float64                `json:"x"`
    Y float64                `json:"y"`
}

type Triangle struct {
    A Vector2
    B Vector2
    C Vector2
}

type Line struct {
    X Vector2
    Y Vector2
}
//全局变量
var triangleArea = float64(0)    //所有三角片面积
var n_getAcreage = 0
func GetAcreage(triangles []Triangle) float64 {
    triangleArea = 0
    n_getAcreage = len(triangles)
    for i:=0;i<n_getAcreage-1 ;i++  {
        for j:=i+1;j< n_getAcreage;j++  {
            triangleA:=triangles[i]
            triangleB:=triangles[j]
            if QuickCross(TriangleToList(triangleA),TriangleToList(triangleB)) {        //快速判断,不准确,后面还需再次判断
                GetShadow(TriangleToList(triangleA),TriangleToList(triangleB),triangles,1,j+1)
            }
        }
    }

}

func GetShadow(list1 []Vector2,list2 []Vector2,trangles []Triangle,sign float64,index int){
    //两个多边形求遮挡多边形和遮挡面积
    var list []Vector2           //相交点集
    list1 = ClockwiseSortPoints(list1)
    list2 = ClockwiseSortPoints(list2)
    //求相交点
    for i:=0;i< len(list1);i++  {
        for j:=0;j<len(list2);j++  {
            var line1,line2 Line
            if i==len(list1)-1 {
                line1.X = list1[0]
                line1.Y = list1[i]
            }else {
                line1.X = list1[i]
                line1.Y = list1[i+1]
            }
            if j==len(list2)-1 {
                line2.X = list2[0]
                line2.Y = list2[j]
            }else {
                line2.X = list2[j]
                line2.Y = list2[j+1]
            }
            point := CrossPoint(line1.X,line1.Y,line2.X,line2.Y)
            if point[0].X!=-1000 && point[0].Y!=-1000{
                list = append(list, point...)
            }
        }
    }
    //求内部点
    for _,v1 := range list1{
        if IsInNedge(list2,v1) {
            list = append(list, v1)
        }
    }
    for _,v2 := range list2{
        if IsInNedge(list1,v2) {
            list = append(list, v2)
        }
    }
    //去重
    list = DeduplicationVec2(list)
    if len(list)<3 {
        return
    }

    shadow := GetNedgeArea(ClockwiseSortPoints(list))
    triangleArea = triangleArea+math.Pow(-1,sign)*shadow
    for k:=index;k<n_getAcreage ;k++  {
        if QuickCross(list,TriangleToList(trangles[k])){
            GetShadow(list,TriangleToList(trangles[k]),trangles,sign+1,k+1)
        }
    }
    return
}

func (this *Vector2)Set(x, y float64) {
    this.X = x
    this.Y = y
}

func (this *Vector2)Equal(x Vector2) bool {
    if math.Abs(this.X - x.X)<0.1 && math.Abs(this.Y - x.Y)<0.1{
        return true
    } else {
        return false
    }
}

func TriangleToList(triangle Triangle) []Vector2 {
    var res []Vector2
    var a Vector2
    a.Set(triangle.A.X,triangle.A.Y)
    res = append(res, a)

    a.Set(triangle.B.X,triangle.B.Y)
    res = append(res, a)

    a.Set(triangle.C.X,triangle.C.Y)
    res = append(res, a)
    return res
}

func QuickCross(list1,list2 []Vector2) bool {
    //判断两多边形是否相交
    var maxx1,maxy1,minx1,miny1,maxx2,maxy2,minx2,miny2 float64
    for _,v1 :=range list1{
        if v1.X>=maxx1 {
            maxx1 = v1.X
        }
        if v1.X<=minx1 {
            minx1 = v1.X
        }
        if v1.Y>=maxy1 {
            maxy1 = v1.Y
        }
        if v1.Y<=miny1 {
            miny1 = v1.Y
        }
    }
    for _,v2 :=range list2{
        if v2.X>=maxx2 {
            maxx2 = v2.X
        }
        if v2.X<=minx2 {
            minx2 = v2.X
        }
        if v2.Y>=maxy2 {
            maxy2 = v2.Y
        }
        if v2.Y<=miny2 {
            miny2 = v2.Y
        }
    }
    Lx := math.Abs((minx1+maxx1)/2-(minx2+maxx2)/2)
    Ly := math.Abs((miny1+maxy1)/2-(miny2+maxy2)/2)
    if Lx<math.Abs(maxx1-minx1)+math.Abs(maxx2-minx2) && Ly<math.Abs(maxy1-miny1)+math.Abs(maxy2-miny2) {
        return true
    }else {
        return false
    }
}

func GetNedgeArea(list []Vector2) float64 {
    //得到任意N边型面积 前提是点的排列方向为顺时针或逆时针
    var x,y float64
    for _,v := range list{
        x+=v.X
        y+=v.Y
    }
    var point Vector2
    point.Set(x/ float64(len(list)),y/ float64(len(list)))
    res := float64(0)
    for q:=0;q< len(list);q++  {
        if q==len(list)-1 {
            res+= GetTriangleAreaByVector(point,list[q],list[0])
        }else {
            res+= GetTriangleAreaByVector(point,list[q],list[q+1])
        }
    }
    return res
}

func ClockwiseSortPoints(list []Vector2) []Vector2 {
    //多边形点集排序--针对凸多边形,按逆时针方向进行排序
    var res []Vector2
    var center Vector2
    var a Vector2
    var first []Vector2
    var second []Vector2
    var third []Vector2
    var four []Vector2
    a.Set(1,0)
    x := float64(0)
    y := float64(0)
    for i:=0;i < len(list);i++{
        x += list[i].X
        y += list[i].Y
    }
    center.X = x/ float64(len(list))
    center.Y = y/ float64(len(list))
    for _,v := range list{
        var b Vector2
        b.Set(v.X-center.X,v.Y-center.Y)
        if b.X>=0 && b.Y>0 {
            first = append(first, b)
        }
        if b.X<0 && b.Y>=0 {
            second = append(second, b)
        }
        if b.X<=0 && b.Y<0 {
            third = append(third, b)
        }
        if b.X>0 && b.Y<=0 {
            four = append(four, b)
        }
    }
    if first!=nil {
        sort1(first)
        for _,v := range first{
            v.X = v.X+center.X
            v.Y = v.Y+center.Y
            res = append(res, v)
        }
    }
    if second!=nil {
        sort1(second)
        for _,v := range second{
            v.X = v.X+center.X
            v.Y = v.Y+center.Y
            res = append(res, v)
        }
    }
    if third!=nil {
        sort2(third)
        for _,v := range third{
            v.X = v.X+center.X
            v.Y = v.Y+center.Y
            res = append(res, v)
        }
    }
    if four!=nil {
        sort2(four)
        for _,v := range four{
            v.X = v.X+center.X
            v.Y = v.Y+center.Y
            res = append(res, v)
        }
    }
    return res
}

func sort1(buf[]Vector2)  {
    //从小到大排序
    var a Vector2
    a.Set(1,0)
    for i := 0; i < len(buf)-1; i++ {
        flag := false
        for j := 1; j < len(buf)-i; j++ {
            if GetAngelByVector2(buf[j-1],a) > GetAngelByVector2(buf[j],a) {
                tmp := buf[j-1]
                buf[j-1] = buf[j]
                buf[j] = tmp
                flag = true
            }
        }
        if !flag {
            break
        }
    }
}

func sort2(buf[]Vector2)  {
    //从大到小排序
    var a Vector2
    a.Set(1,0)
    for i := 0; i < len(buf)-1; i++ {
        flag := false
        for j := 1; j < len(buf)-i; j++ {
            if GetAngelByVector2(buf[j-1],a) < GetAngelByVector2(buf[j],a) {
                tmp := buf[j-1]
                buf[j-1] = buf[j]
                buf[j] = tmp
                flag = true
            }
        }
        if !flag {
            break
        }
    }
}

func GetAngelByVector2(a,b  Vector2) float64 {
    //二维得到两向量夹角
    a_sqrt := math.Sqrt(a.X*a.X + a.Y*a.Y)
    b_sqrt := math.Sqrt(b.X*b.X + b.Y*b.Y)
    cos := (a.X*b.X + a.Y*b.Y)/(a_sqrt*b_sqrt)
    res := math.Acos(cos)
    return res
}

func DeduplicationVec2(crossPoint []Vector2) []Vector2 {
    //去重
    if crossPoint==nil {
        return crossPoint
    }
    var res []Vector2
    var temp = true
    res = append(res, crossPoint[0])
    for _,v1 := range crossPoint{
        for _,v2:= range res{
            if v2.Equal(v1) {
                temp = false
                break
            }
        }
        if temp {
            res = append(res, v1)
        }
        temp = true
    }
    return res
}

func IsInNedge(list []Vector2,point Vector2) bool {
    nArea := GetNedgeArea(list)
    var a float64
    for i:=0;i<len(list) ;i++  {
        if i==len(list)-1 {
            a+=GetTriangleAreaByVector(point,list[i],list[0])
        }else {
            a+=GetTriangleAreaByVector(point,list[i],list[i+1])
        }
    }
    if math.Abs(a-nArea)<0.1 {
        return true
    }else {
        return false
    }
}
func CrossPoint(line1,line2,line3,line4 Vector2) []Vector2 {
    var res []Vector2
    if line1.Equal(line3) && line2.Equal(line4) {
        res = append(res, line1)
        res = append(res, line2)
        return res
    }

    if line1.Equal(line4) && line2.Equal(line3) {
        res = append(res, line1)
        res = append(res, line2)
        return res
    }

    if line1.Equal(line3) && !line2.Equal(line4) {
        res = append(res,line1)
        return res
    }

    if line1.Equal(line4) && !line2.Equal(line3) {
        res = append(res,line1)
        return res
    }
    if line2.Equal(line3) && !line1.Equal(line4) {
        res = append(res,line2)
        return res
    }
    if line2.Equal(line4) && !line1.Equal(line3) {
        res = append(res,line2)
        return res
    }
    var cross_point Vector2
    var a = float64(0)
    var b = float64(0)
    var state = 0
    if math.Abs(line1.X - line2.X)>1e-6 {
        a = (line2.Y - line1.Y) / (line2.X - line1.X)
        state |= 1
    }
    if math.Abs(line3.X - line4.X)>1e-6 {
        b = (line4.Y - line3.Y) / (line4.X - line3.X)
        state |= 2
    }
    switch state {
    case 0: //L1与L2都平行Y轴
        if math.Abs(line1.X - line3.X)<1e-6{
            //throw new Exception("两条直线互相重合,且平行于Y轴,无法计算交点。");
            cross_point.Set(-1000,-1000)
        }else{
            //throw new Exception("两条直线互相平行,且平行于Y轴,无法计算交点。");
            cross_point.Set(-1000,-1000)
        }
    case 1:    //L1存在斜率, L2平行Y轴
        x := line3.X
        y := (line1.X - x) * (-a) + line1.Y
        cross_point.Set(x,y)
    case 2://L1 平行Y轴,L2存在斜率
        x := line1.X
        y := (line3.X - x) * (-b) + line3.Y
        cross_point.Set(x,y)
    case 3://L1,L2都存在斜率
        if math.Abs(a - b)<1e-6 {
            // throw new Exception("两条直线平行或重合,无法计算交点。");
            cross_point.Set(-1000,-1000)
        }
        x := (a * line1.X - b * line3.X - line1.Y + line3.Y) / (a - b)
        y := a * x - a * line1.X + line1.Y
        cross_point.Set(x,y)
    }
    //beego.Debug(cross_point)
    if !((OnSegment(line1,line2,cross_point)) && (OnSegment(line3,line4,cross_point))){
        cross_point.Set(-1000,-1000)
    }
    res = append(res, cross_point)
    return res  //没有交点返回(-1000,-1000)
}

func GetTriangleAreaByVector(x Vector2,y Vector2,z Vector2) float64 {
    //根据三点坐标获得三角形面积
    area := (x.X*y.Y+y.X*z.Y+z.X*x.Y-x.X*z.Y-y.X*x.Y-z.X*y.Y)/2
    if area<0 {
        area = -area
    }
    return area
}

func OnSegment( p1,p2,Q Vector2) bool {
    //判断一个点是不是在一个线段内

    maxx := math.Max(p1.X,p2.X)
    minx := math.Min(p1.X,p2.X)
    maxy := math.Max(p1.Y,p2.Y)
    miny := math.Min(p1.Y,p2.Y)
    index := (Q.X -p1.X )*(p2.Y -p1.Y) - (p2.X -p1.X) *(Q.Y -p1.Y)
    if index<=1e-5 && ( (Q.X > minx || math.Abs(Q.X-minx)<1e-5) && (Q.X < maxx || math.Abs(Q.X-maxx)<1e-5)&&
        (Q.Y > miny || math.Abs(Q.Y-miny)<1e-5) && (Q.Y < maxy || math.Abs(Q.Y-maxy)<1e-5)) {
        return true
    }else {
        return false
    }
}

参考文献:徐元铭,龙伟,王永庆.军机易损性分析中多重遮挡投影面积计算[J].北京航空航天大学学报,2002(02):245-248.

相关链接:http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CJFQ&dbname=CJFD2002&filename=BJHK200202032&uid=WEEvREcwSlJHSldRa1FhdkJkVG1BVnRVeWZHNVVIbWExVGhucVdORzVtVT0=$9A4hF_YAuvQ5obgVAqNKPCYcEjKensW4IQMovwHtwkF4VYPoHbKxJw!!&v=MzIwOTVnVmIzSUp5ZkRaYkc0SHRQTXJZOUdab1I4ZVgxTHV4WVM3RGgxVDNxVHJXTTFGckNVUkxPZlkrZHBGeXo=

原文地址:https://www.cnblogs.com/zheng123/p/10717644.html

时间: 2024-10-06 20:40:03

golang 二维平面求多重遮挡三角形总面积的相关文章

二维平面上判断点在三角形内的最优算法

园子里有很多关于点是否在三角形内的文章,提供了各种方法.这让人很纠结,到底该用哪种算法?这里提供一套我认为最优的算法.如果你有不同的意见,亦或有更好的算法,欢迎来讨论. 算法使用的是同向法,其原理是:假设点P位于三角形ABC内,会有这样一个规律:三角形的每一个边,其对角点与P在边的同一侧:或者说三角形的每一个顶点与P在其对角边的同一侧. (1)代码 // 2D vector class Vec2 { public: Vec2() { x = 0.0f; y = 0.0f; } Vec2(floa

POJ 3241 Object Clustering 二维平面曼哈顿距离最小生成树

题目链接:点击打开链接 题意: 给定二维平面上的n个点坐标,常数k 下面n行给出坐标 求一个最小生成树,问第k大的边是多少. 任意两个点间建一条边的花费是其曼哈顿距离. 思路:转自:点击打开链接 一.曼哈顿距离最小生成树 曼哈顿距离最小生成树问题可以简述如下: 给定二维平面上的N个点,在两点之间连边的代价为其曼哈顿距离,求使所有点连通的最小代价. 朴素的算法可以用O(N2)的Prim,或者处理出所有边做Kruskal,但在这里总边数有O(N2)条,所以Kruskal的复杂度变成了O(N2logN

数字之魅:寻找二维平面上的最近的点对

在二维平面上的n个点中,如何快速的找出最近的一对点,就是最近点对问题. 初看这个题,可能感觉有点儿复杂. 方案一:蛮力法.数组中总共包含N个数,所以我们可以把平面内所有的点按X轴排序,然后依次算出后一个坐标与前面所有左边的距离,然后用Min和position来记录最近的距离和两个坐标.该方案和在一维空间求两个最近点的距离有点儿类似,其时间复杂度为:O(N*N). 方案二:在一维空间里,我们知道如果数组有序,我们可以很快找出最近的两个点.我们可以用O(N*logN)的时间复杂度来对数据进行排序[快

给定二维平面整数点集输出“最大点集”算法(今日头条面试题)

引子 最近自己的独立游戏上线了,算是了却了一桩心愿.接下来还是想找一份工作继续干,创业的事有缘再说. 找工作之前,总是要浏览一些实战题目,热热身嘛.通过搜索引擎,搜到了今日头条的一道面试题. 题目 P为给定的二维平面整数点集.定义 P 中某点x,如果x满足 P 中任意点都不在 x 的右上方区域内(横纵坐标都大于x),则称其为"最大的".求出所有"最大的"点的集合.(所有点的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) 内) 如下图:实心点为满足条件的点的集

二维平面最近点-分治

题目描述给出二维平面上的n个点,求其中最近的两个点的距离的一半.输入包含多组数据,每组数据第一行为n,表示点的个数:接下来n行,每行一个点的坐标.当n为0时表示输入结束,每组数据输出一行,为最近的两个点的距离的一半.输入样例:20 01 121 11 13-1.5 00 00 1.50输出样例:0.710.000.75题目解析:采用分治的思想,把n个点按照x坐标进行排序,以坐标mid为界限分成左右两个部分, 对左右两个部分分别求最近点对的距离,然后进行合并.对于两个部分求得的最近距离d, 合并过

C/C++调用Golang 二

C/C++调用Golang 二 <C/C++调用Golang 一>简单介绍了C/C++调用Golang的方法步骤,只涉及一个简单的函数调用.本文总结具体项目中的使用场景,将介绍三种较复杂的调用方式:一,C++向golang传入复杂结构体:二,C++向golang传入回调函数,在golang中调用C++函数:三,C++调用golang函数,返回复杂的结构体. (本文后面涉及三个例子,省略了编译步骤,仅展示关键代码.具体操作步骤参考<C/C++调用Golang 一>) 一 C++向go

二维平面曼哈顿距离最小生成树模版

#include <stdio.h> #include <iostream> #include <algorithm> #include <sstream> #include <stdlib.h> #include <string.h> #include <limits.h> #include <vector> #include <string> #include <time.h> #i

9.7数学与概率(三)——在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分

/** * 功能:在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分. * 假定正方形的上下两条边与x轴平行. */ /** * 考虑: * 线的准确含义,可能性有: * 1)由斜率和y轴截距确定: * 2)由这条边上的任意两点确定: * 3)线段,以正方形的边作为起点和终点. * * 假设:这条线的端点应该落在正方形的边上. * 思路:要将两个正方形对半分,这条线必须连接两个正方形的中心点. */ public class Square { //正方形的四条边 int lef

(hdu step 2.2.5)三角形(求n个三角形能把平面分成多少部分)

题目: 三角形 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 653 Accepted Submission(s): 493   Problem Description 用N个三角形最多可以把平面分成几个区域? Input 输入数据的第一行是一个正整数T(1<=T<=10000),表示测试数据的数量.然后是T组测试数据,每组测试数据只包含