CodeArchive Geometry

From Ubcacm
Jump to: navigation, search

Geometry

Includes/typedefs.

#include <algorithm>
#include <iostream>
#include <vector>
#include <complex>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef complex < ll > point;
typedef complex < ld > fpoint;
typedef vector < point > pol;

Integer geometry. Please punch me as hard as possible if I try to use floating point while the problem can be done in integers.

inline ll sign(const ll n){
  return !n ? 0 : (n < 0 ? -1 : 1); 
}

inline ll cp(const point &v1, const point &v2){
  return v1.real()*v2.imag() - v1.imag()*v2.real();
}

inline ll dp(const point &v1, const point &v2){
  return v1.real()*v2.real() + v1.imag()*v2.imag();
}

pair < ull, ull > get_absr(const point &a, const point &b, const point &c, const point &d){
  return make_pair(abs(cp(a - c, d - c)), abs(cp(d - c, b - a)));
}

inline bool ray_crosses_point(const point &r1, const point &r2, const point &p){
  return dp(r2 - r1, p - r1) >= 0 && cp(r2 - r1, p - r1) == 0; // in front and on the same line
}

// Prereqs: non-parallel, (non-zero). Excluding end points of the segment!!!
inline bool line_crosses_segment(const point &l1, const point &l2, const point &s1, const point &s2){
  return sign(cp(l2 - l1, s1 - l1)) == -sign(cp(l2 - l1, s2 - l1)); // on opposite sides
} 

// Prereqs: non-parallel, non-zero. Excluding end points of the segment, but including the beginning of the ray
inline bool ray_crosses_segment(const point &r1, const point &r2, const point &s1, const point &s2){
  return line_crosses_segment(r1, r2, s1, s2) && sign(cp(r1 - s1, s2 - s1))*sign(cp(s2 - s1, r2 - r1)) >= 0;
}

// Assumes counter-clockwise orientation. Did ray r1 -> r2 enter/leave the polygon while going through p2 ?
inline ll in_out_point(const point &r1, const point &r2, const point &p1, const point &p2, const point &p3){
  ll s1 = sign(cp(r2 - r1, p1 - r1)), s3 = sign(cp(r2 - r1, p3 - r1));
  if(s1 == s3) return 0;              // no change
  if(s1 == 1 && s3 == -1) return 1;   // entering 
  if(s1 == -1 && s3 == 1) return -1;  // leaving
  ll d1 = dp(r2 - r1, p1 - r1), d2 = dp(r2 - r1, p2 - r1), d3 = dp(r2 - r1, p3 - r1);
  if(s3 == 0 && d3 > d2 && s1 > 0 || s1 == 0 && d2 < d1 && s3 < 0) return 1;
  if(s3 == 0 && d3 < d2 && s1 < 0 || s1 == 0 && d2 > d1 && s3 > 0) return -1;
  return 0;
}

// Assumes counter-clockwise orientation, non-parallel, intersection takes place somewhere in the middle.
inline ll in_out_segment(const point &r1, const point &r2, const point &s1, const point &s2){
  return cp(r2 - r1, s1 - r1) > 0 ? 1 : -1;
}

ull mb = (1ll << 31); // mult. base, 1*2 bits to catch overflow
inline pair < ull, ull > mult(ull a, ull b){
  ull low = (a & (mb - 1))*(b & (mb - 1));
  ull middle = (a >> 31)*(b & (mb - 1)) + (a & (mb - 1))*(b >> 31);
  low += ((middle & (mb - 1)) << 31);
  return make_pair((a >> 31)*(b >> 31) + (middle >> 31) + (low >> 62), low & ((1ll << 62) - 1)); 
}

Floating point geometry - use it carefully

#define EPS 1e-9
const ld PI = acos((ld)-1);

inline ld cp(const point &v1, const point &v2){
  return v1.real()*v2.imag() - v1.imag()*v2.real();
}

inline ld dp(const point &v1, const point &v2){
  return v1.real()*v2.real() + v1.imag()*v2.imag();
}

// floating point sign ;)  *TESTED ON 10902*
inline ld sign(const ld n){
  return n > EPS ? 1 : (n < -EPS ? -1 : 0); 
}

// includes end points  *TESTED ON 10902*
bool segment_crosses_segment(const fpoint &a1, const fpoint &a2, const fpoint &b1, const fpoint &b2){
  return sign(cp(a2 - a1, b1 - a1))*sign(cp(a2 - a1, b2 - a1)) <= 0 &&
    sign(cp(b2 - b1, a1 - b1))*sign(cp(b2 - b1, a2 - b1)) <= 0;
} 

// *TESTED ON 303*
inline point line_inter(const point &a, const point &b, const point &c, const point &d){
  return a + cp(a - c, d - c)/cp(d - c, b - a) * (b - a);
}

inline ld get_r(const point &a, const point &b, const point &c, const point &d){
  return cp(a - c, d - c) / cp(d - c, b - a);
}

// SIGNED line to point distance. Point on the right of vector (a -> b) will be NEGATIVE.
inline ld lp_dist(const point &a, const point &b, const point &p){
  return cp(b - a, p - a) / abs(b - a);
}

// Projection of line segment from a to p to vector (a -> b), SIGNED - positive in front
inline ld proj_dist(const point &a, const point &b, const point &p){
  return dp(b - a, p - a) / abs(b - a);
}

// Line segment (a, b) to point p distance.
inline ld lsp_dist(const point &a, const point &b, const point &p){
  return proj_dist(a, b, p) > 0 && proj_dist(b, a, p) > 0 ?
    abs(lp_dist(a, b, p)) : min(abs(a - p), abs(b - p));
}

// Closest point on line segment (a, b) to point p.
inline point cp_segment(const point &a, const point &b, const point &p){
  if(proj_dist(a, b, p) > 0 && proj_dist(b, a, p) > 0)
    return abs(lp_dist(a, b, p)) < EPS ? p : line_inter(a, b, p, p + perp(a - b));
  return abs(a - p) < abs(b - p) ? a : b;
}

// Left to the vector (a -> b) will be cut off. Erases duplicate points.
pol cut_polygon(const pol &v, const point &a, const point &b){
  pol out;
  for(int j = 0, i = v.size() - 1; j < (int)v.size(); i = j++){
    if( lp_dist(a, b, v[i]) < EPS ) out.push_back(v[i]);
    if( lp_dist(a, b, v[i]) < -EPS && lp_dist(a, b, v[j]) > EPS ||
   lp_dist(a, b, v[i]) > EPS && lp_dist(a, b, v[j]) < -EPS ){
      point p = line_inter(a, b, v[i], v[j]);
      if(!out.size() || abs(out.back() - p) > EPS) out.push_back(p);
    }
  }
  while(out.size() && abs(out[0] - out.back()) < EPS) out.pop_back();
  return out;
}

// Area of a polygon (convex or concave). Always non-negative.
ld area(pol v){
  ld out = 0;
  for(int j = 0, i = v.size() - 1; j < (int)v.size(); i = j++) out += cp(v[i], v[j]);
  return abs(out)/2;
}

// Lexicographical comparison for points.
bool cmp_lex(const point &a, const point &b){
  if(a.real() == b.real()) return a.imag() < b.imag();
  return a.real() < b.real();
}

// Coparison by angle relative to the base (primary) and distance (secondary)
// WARNING!!! NORM is the square of length, watch for overflow!!!!!!!!!!!!!!!
point base;
bool cmp_base(const point &a, const point &b){
  if(cp(a - base, b - base) == 0) return norm(a - base) < norm(b - base);
  return cp(a - base, b - base) < 0;
}

// Finds CONVEX HULL of a polygon. Might not (will not) work with floating point.
pol convex_hull(pol v){
  sort(v.begin(), v.end(), cmp_lex), base = v[0];
  sort(v.begin() + 1, v.end(), cmp_base);
  
  pol out;
  for(int i = 0; i < (int)v.size(); i++){
    while(out.size() > 1 && cp(out.back() - out[out.size() - 2], v[i] - out[out.size() - 2]) >= 0) out.pop_back();
    out.push_back(v[i]);
  }
  return out;
}

// Point on line segment. Checks if p lies on line segment (a, b).
inline bool on_segment(const point &a, const point &b, const point &p){
  return proj_dist(a, b, p) > -EPS && proj_dist(b, a, p) > -EPS && abs(lp_dist(a, b, p)) < EPS;
}

// Point on the boundry of a polygon
bool on_boundry(const pol &v, const point &p){
  for(int i = v.size() - 1, j = 0; j < (int)v.size(); i = j++)
    if(on_segment(v[i], v[j], p)) return true;
  return false;
}

// I assume that the point is not on any of the line segments...
// the following two functions were tested on 881 and 10867 !!!!!!!!!!!!!
ld get_chains_angle(const pol &v, const point &p){
  ld out = 0;
  for(int i = v.size() - 1, j = 0; j < (int)v.size(); i = j++)
    out += atan2(cp(v[i] - p, v[j] - p), dp(v[i] - p, v[j] - p));
  return out;
}
// orientation does not matter !!! :)
// tested on 881 *!!!NO BOUNDRY CHECKING!!!!!*
bool point_in_polygon(const pol &v, const point &p){
  return abs(get_chains_angle(v, p)) > PI; // will be either 2*PI or 0
}

// intersection of two (could be concave) polygons
// !!! not tested on any UVA problems !!! but looks to work
vector < pol > pol_inter(pol v1, pol v2){
  vector < pair < point, point > > segm;

  for(int k = 0; k < 2; k++){
    for(int i = v1.size() - 1, j = 0; j < (int)v1.size(); i = j++){
      pol p; p.push_back(v1[i]), p.push_back(v1[j]);
      for(int ii = v2.size() - 1, jj = 0; jj < (int)v2.size(); ii = jj++)
   if(abs(cp(v1[i] - v1[j], v2[ii] - v2[jj])) > EPS){
     ld a = get_r(v1[i], v1[j], v2[ii], v2[jj]); 
     ld b = get_r(v2[ii], v2[jj], v1[i], v1[j]);
     if(a > -EPS && a < 1 + EPS && b > -EPS && b < 1 + EPS) p.push_back(line_inter(v1[i], v1[j], v2[ii], v2[jj]));
   }
      sort(p.begin(), p.end(), cmp_lex);
      for(int i = 0; i + 1 < (int)p.size(); i++){
   point a = (p[i] + p[i + 1])/ld(2);
   if(on_boundry(v2, a) || point_in_polygon(v2, a)) segm.push_back(make_pair(p[i], p[i + 1]));
      }
    }
    v1.swap(v2);
  }

  vector < pol > out;
  vector < bool > vis(segm.size(), false);
  for(int i = 0; i < (int)segm.size(); i++)
    if(!vis[i]){
      out.resize(out.size() + 1), out.back().push_back(segm[i].first), out.back().push_back(segm[i].second);
      point now = segm[i].second;
      while(true){
   bool done = true;
   for(int j = i + 1; j < (int)segm.size(); j++)
     if(!vis[j]){
       if(abs(segm[j].first - now) < EPS){
         done = false, now = segm[j].second, vis[j] = 1;
         break;
       }else if(abs(segm[j].second - now) < EPS){
         done = false, now = segm[j].first, vis[j] = 1;
         break;
       } 
     }
   if(done) break;
   out.back().push_back(now);
      }
      if(abs(out.back().back() - out.back()[0]) < EPS) out.back().pop_back();
    }
  return out;
}

// Closest pair specific comparison - imag has priority
// Tested on 10245, 10750
struct cmp_cp_st{
  bool operator()(const point &p1, const point &p2) const{
    return p1.imag() == p2.imag() ? p1.real() < p2.real() : p1.imag() < p2.imag();
  }
};

// Closest pair in n*log(n). Returns the distance.
// Modify apropriately if want it to work in ints.
// Tested on 10245, 10750
ld closest_pair(vector < point > v){
  sort(v.begin(), v.end(), cmp_lex);
  ld best = 1e99; int low = 0;
  
  set < point, cmp_cp_st > help;
  for(int i = 0; i < (int)v.size(); i++){
    while(low < i && (v[i] - v[low]).real() > best)
      help.erase(v[low++]);
    for(__typeof(help.end()) it = help.lower_bound(v[i] - point(1e99, best));
   it != help.end() && (*it - v[i]).imag() < best; it++) best = min(best, abs(*it - v[i]));
    help.insert(v[i]);
  }
  
  return best;
}

// given two circles, find all tangent angles.
// returns four angles for the first circle.
inline vector < ld > get_tangents(const point &p1, const ld &r1, const point &p2, const ld &r2){
  point v = p2 - p1;                    // vector from p1 to p2 
  ld d = abs(v);                        // distance between centers
  ld a = acos((r1 - r2)/d);             // outer tangent's angle relative to circle1 -> circle2
  ld b = acos((r1 + r2)/d);             // inner .......
  ld s = atan2(v.imag(), v.real());     // basis angle relative to the circle1
  vector < ld > out;
  out.push_back(s + a), out.push_back(s - a);
  out.push_back(s + b), out.push_back(s - b);
  return out;
}

ld good_angle(ld a){
  return fmod(fmod(a, 2*PI) + 2*PI, 2*PI);
}

ld ang_dist(ld a1, ld a2){
  ld d = fmod(abs(a1 - a2), 2*PI);
  return min(d, 2*PI - d);
}

// looks to work in N^3
vector < vector < pair < int, ld > > > build_graph(const vector < pair < point, ld > > &data){
  vector < ld > angle(4*data.size()*data.size(), 0), temp;
  
  for(int i = 0; i < (int)data.size(); i++)
    for(int j = 0; j < (int)data.size(); j++)
      if(i != j){
   temp = get_tangents(data[i].first, data[i].second, data[j].first, data[j].second);
   for(int k = 0; k < 4; k++) angle[4*i*data.size() + 4*j + k] = temp[k];      // i -> j
      }
  
  vector < point > pnts;
  for(int i = 0; i < (int)angle.size(); i++){
    point p = data[i/(4*data.size())].first;
    ld r =  data[i/(4*data.size())].second;
    ld a = angle[i];
    pnts.push_back(p + r*point(cos(a), sin(a)));
  }

  vector < vector < pair < int, ld > > > out(pnts.size());
  // do the distances between points on same circle
  for(int i = 0; i < (int)angle.size(); i += 4*data.size()){ // for each circle
    int id = i/(4*data.size()); // circle #
    vector < pair < ld, int > > help;
    for(int j = 0; j < (int)data.size()*4; j++)
      help.push_back(make_pair(good_angle(angle[i + j]), i + j));
    sort(help.begin(), help.end());
    for(int a = help.size() - 1, b = 0; b < (int)help.size(); a = b++)
      out[help[a].second].push_back(make_pair(help[b].second, data[id].second*ang_dist(help[a].first, help[b].first))),
   out[help[b].second].push_back(make_pair(help[a].second, data[id].second*ang_dist(help[a].first, help[b].first)));
  }

  // do the distances between different circles, whenever possible...
  for(int i = 0; i < (int)data.size(); i++)
    for(int j = i + 1; j < (int)data.size(); j++)
      for(int k = 0; k < 4; k++){
   int ii = 4*i*data.size() + 4*j + k;
   int jj = 4*j*data.size() + 4*i + (k^1^((k&2) >> 1));
   
   bool good = true; // check is line if far enough from each circle
   for(int kk = 0; kk < (int)data.size() && good; kk++)
     good &= (lsp_dist(pnts[ii], pnts[jj], data[kk].first) + EPS >= data[kk].second);
   
   if(good){
     out[ii].push_back(make_pair(jj, abs(pnts[ii] - pnts[jj])));
     out[jj].push_back(make_pair(ii, abs(pnts[ii] - pnts[jj])));
   }
      }
  
  return out;
}

-- Main.YuryKholondyrev - 29 Jan 2006