sgu 124

给定2D平面上的一个闭合多边形, 和一个不在多边形边界上的点p0(x0, y0), 想要知道点是在外面还是里面
方法是射线法, 就是做一条以p0为起点的射线, 射向任意一个方向, 如果射线和多边形的交点数目为奇数, 说明p0在里面, 否则在外面.
所以,我们可以枚举多边形的每一条边界,看看射线是不是穿过,把射线穿过的边界数统计出来。
原理理解不难, 关键是有些细节需要注意。如下图的c, d, e, f

为了方便,不妨规定射线总是平行于x轴,而且从p0射向负无穷的方向。
假定现在我们画出了那条从p0射出的射线,称作originL
如果originL穿过了多边形的顶点(也就是多边形的某两条边界的公共端点)或者穿过多边形上某些平行于x轴的边界重合的话,会导致我们最后计算出来的交点数错误,为了消除这种特殊情况,我们采取一个特别的策略。

先引入两个定理。
定理1:当p0不在多边形的边界上的时候,总是存在一个足够小的正数e1,使得“p0上移距离e以后,p0与多边形的位置关系不变”满足
 定理2: 当originL穿过了多边形的顶点(也就是多边形的某两条边界的交点)或者和多边形上那些平行于x轴的边界重合,总是存在一个足够小的正数e2, 使得“originL上移距离e2以后,originL不穿过多边形的任意顶点并且不穿过多边形的任意一条与x轴平行的边界”满足

 这两个定理的正确性不难想象出来。

 我们先找出一个恰当的e,这个e是根据定理1和定理2里得到的e1和e2的较小值,也就是,e = min{e1,e2},这个e的具体数值算法里用不到,但是这个数的存在性是正确的。我们将p0上移e,然后画出一条新的射线newL,这样,我们就把问题转化为求newL与多边形的交点数目,而且,newL是不会出现“穿过了多边形的顶点或者穿过了多边形上那些平行于x轴的边界”的特殊情况的!

 但是, 由于直接计算一个恰当的e非常困难, 所以要用间接的方法判断newL与某条边界的关系。

具体的流程是:

枚举多边形的每条边界E:
=> 如果E是与x轴平行的,那么newL和E不相交
=> 否则,如果E不是在p0的左侧, 那么newL和E不相交。
=> 否则, 如果originL穿过E的下端点 或者 穿过E的下端点和上端点的中间部分(不包括上端点),那么newL和E有一个交点。
=> 否则, newL和E没有交点

 

 

#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

struct Seg {
  int x1, y1, x2, y2;
};

Seg newSeg(int x1, int y1, int x2, int y2) {
  Seg s;
  s.x1 = x1;
  s.y1 = y1;
  s.x2 = x2;
  s.y2 = y2;
  return s;
}

//=======================
vector< Seg > xSegs; //与x轴平行的那些Segment
vector< Seg > ySegs;
int N;
int tX, tY;

//=============================

bool belongs() {
  Seg cur;
  int i;
  for (i = 0; i < xSegs.size(); ++i) {
    cur = xSegs[i];
    if (tY == cur.y1 && cur.x1 <= tX && tX <= cur.x2) return true;
  }
  for (i = 0; i < ySegs.size(); ++i) {
    cur = ySegs[i];
    if (tX == cur.x1 && cur.y1 <= tY && tY <= cur.y2) return true;
  }

  return false;
}


bool inside() {
  int i;
  Seg cur;

  int crossNum = 0;
  for (i = 0; i < ySegs.size(); ++i) {
    cur = ySegs[i];
    if (cur.x1 < tX && cur.y1 <= tY && tY < cur.y2) ++crossNum;  // <= 注意看这里 tY < cur.y2
  }

  if (crossNum % 2 == 1) return true;
  else return false;
}


void input() {
  scanf("%d", &N);
  int i;
  int x1, x2, y1, y2;
  xSegs.clear();
  ySegs.clear();
  for (i = 0; i < N; ++i) {
    scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
    if (x1 == x2) {
      if (y1 > y2) swap(y1, y2);
      ySegs.push_back(newSeg(x1, y1, x2, y2));
    }
    else {
      if (x1 > x2) swap(x1, x2);
      xSegs.push_back(newSeg(x1, y1, x2, y2));
    }
  }

  scanf("%d %d", &tX, &tY);
}


int main() {
  input();
  if (belongs()) {
    printf("BORDER\n");
  } else if (inside()) {
    printf("INSIDE\n");
  } else {
    printf("OUTSIDE\n");
  }
  return 0;
}

转载于:https://my.oschina.net/mustang/blog/55960

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值