polyg.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /* utilities related to polygons */
  2. #include <cassert>
  3. #include <iostream>
  4. #include "elt.hpp"
  5. #include "errhandle.hpp"
  6. #include "polyg.hpp"
  7. namespace sphereRemap {
  8. using namespace std;
  9. /* given `N` `vertex`es, `N` `edge`s and `N` `d`s (d for small circles)
  10. and `g` the barycentre,
  11. this function reverses the order of arrays of `vertex`es, `edge`s and `d`s
  12. but only if it is required!
  13. (because computing intersection area requires both polygons to have same orientation)
  14. */
  15. void orient(int N, Coord *vertex, Coord *edge, double *d, const Coord &g)
  16. {
  17. Coord ga = vertex[0] - g;
  18. Coord gb = vertex[1] - g;
  19. Coord vertical = crossprod(ga, gb);
  20. if (N > 2 && scalarprod(g, vertical) < 0) // (GAxGB).G
  21. {
  22. for (int i = 0; i < N/2; i++)
  23. swap(vertex[i], vertex[N-1-i]);
  24. for (int i = 0; i < (N-1)/2; i++)
  25. {
  26. swap(edge[N-2-i], edge[i]);
  27. swap(d[i], d[N-2-i]);
  28. }
  29. }
  30. }
  31. void normals(Coord *x, int n, Coord *a)
  32. {
  33. for (int i = 0; i<n; i++)
  34. a[i] = crossprod(x[(i+1)%n], x[i]);
  35. }
  36. Coord barycentre(const Coord *x, int n)
  37. {
  38. if (n == 0) return ORIGIN;
  39. Coord bc = ORIGIN;
  40. for (int i = 0; i < n; i++)
  41. bc = bc + x[i];
  42. /* both distances can be equal down to roundoff when norm(bc) < mashineepsilon
  43. which can occur when weighted with tiny area */
  44. assert(squaredist(bc, proj(bc)) <= squaredist(bc, proj(bc * (-1.0))));
  45. //if (squaredist(bc, proj(bc)) > squaredist(bc, proj(bc * (-1.0)))) return proj(bc * (-1.0));
  46. return proj(bc);
  47. }
  48. /** computes the barycentre of the area which is the difference
  49. of a side ABO of the spherical tetrahedron and of the straight tetrahedron */
  50. static Coord tetrah_side_diff_centre(Coord a, Coord b)
  51. {
  52. Coord n = crossprod(a,b);
  53. double sinc2 = n.x*n.x + n.y*n.y + n.z*n.z;
  54. assert(sinc2 < 1.0 + EPS);
  55. // exact: u = asin(sinc)/sinc - 1; asin(sinc) = geodesic length of arc ab
  56. // approx:
  57. // double u = sinc2/6. + (3./40.)*sinc2*sinc2;
  58. // exact
  59. if (sinc2 > 1.0 - EPS) /* if round-off leads to sinc > 1 asin produces NaN */
  60. return n * (M_PI_2 - 1);
  61. double sinc = sqrt(sinc2);
  62. double u = asin(sinc)/sinc - 1;
  63. return n*u;
  64. }
  65. /* compute the barycentre as the negative sum of all the barycentres of sides of the tetrahedron */
  66. Coord gc_normalintegral(const Coord *x, int n)
  67. {
  68. Coord m = barycentre(x, n);
  69. Coord bc = crossprod(x[n-1]-m, x[0]-m) + tetrah_side_diff_centre(x[n-1], x[0]);
  70. for (int i = 1; i < n; i++)
  71. bc = bc + crossprod(x[i-1]-m, x[i]-m) + tetrah_side_diff_centre(x[i-1], x[i]);
  72. return bc*0.5;
  73. }
  74. Coord exact_barycentre(const Coord *x, int n)
  75. {
  76. if (n >= 3)
  77. {
  78. return proj(gc_normalintegral(x, n));
  79. }
  80. else if (n == 0) return ORIGIN;
  81. else if (n == 2) return midpoint(x[0], x[1]);
  82. else if (n == 1) return x[0];
  83. }
  84. Coord sc_gc_moon_normalintegral(Coord a, Coord b, Coord pole)
  85. {
  86. double hemisphere = (a.z > 0) ? 1: -1;
  87. double lat = hemisphere * (M_PI_2 - acos(a.z));
  88. double lon1 = atan2(a.y, a.x);
  89. double lon2 = atan2(b.y, b.x);
  90. double lon_diff = lon2 - lon1;
  91. // wraparound at lon=-pi=pi
  92. if (lon_diff < -M_PI) lon_diff += 2.0*M_PI;
  93. else if (lon_diff > M_PI) lon_diff -= 2.0*M_PI;
  94. // integral of the normal over the surface bound by great arcs a-pole and b-pole and small arc a-b
  95. Coord sc_normalintegral = Coord(0.5*(sin(lon2)-sin(lon1))*(M_PI_2 - lat - 0.5*sin(2.0*lat)),
  96. 0.5*(cos(lon1)-cos(lon2))*(M_PI_2 - lat - 0.5*sin(2.0*lat)),
  97. hemisphere * lon_diff * 0.25 * (cos(2.0*lat) + 1.0));
  98. Coord p = Coord(0,0,hemisphere); // TODO assumes north pole is (0,0,1)
  99. Coord t[] = {a, b, p};
  100. if (hemisphere < 0) swap(t[0], t[1]);
  101. return (sc_normalintegral - gc_normalintegral(t, 3)) * hemisphere;
  102. }
  103. double triarea(Coord& A, Coord& B, Coord& C)
  104. {
  105. double a = ds(B, C);
  106. double b = ds(C, A);
  107. double c = ds(A, B);
  108. double s = 0.5 * (a + b + c);
  109. double t = tan(0.5*s) * tan(0.5*(s - a)) * tan(0.5*(s - b)) * tan(0.5*(s - c));
  110. // assert(t >= 0);
  111. if (t<1e-20) return 0. ;
  112. return 4 * atan(sqrt(t));
  113. }
  114. /** Computes area of two two-sided polygon
  115. needs to have one small and one great circle, otherwise zero
  116. (name origin: lun is moon in french)
  117. */
  118. double alun(double b, double d)
  119. {
  120. double a = acos(d);
  121. assert(b <= 2 * a);
  122. double s = a + 0.5 * b;
  123. double t = tan(0.5 * s) * tan(0.5 * (s - a)) * tan(0.5 * (s - a)) * tan(0.5 * (s - b));
  124. double r = sqrt(1 - d*d);
  125. double p = 2 * asin(sin(0.5*b) / r);
  126. return p*(1 - d) - 4*atan(sqrt(t));
  127. }
  128. /**
  129. This function returns the area of a spherical element
  130. that can be composed of great and small circle arcs.
  131. The caller must ensure this function is not called when `alun` should be called instaed.
  132. This function also sets `gg` to the barycentre of the element.
  133. "air" stands for area and "bar" for barycentre.
  134. */
  135. double airbar(int N, const Coord *x, const Coord *c, double *d, const Coord& pole, Coord& gg)
  136. {
  137. if (N < 3)
  138. return 0; /* polygons with less then three vertices have zero area */
  139. Coord t[3];
  140. t[0] = barycentre(x, N);
  141. Coord *g = new Coord[N];
  142. double area = 0;
  143. Coord gg_exact = gc_normalintegral(x, N);
  144. for (int i = 0; i < N; i++)
  145. {
  146. /* for "spherical circle segment" sum triangular part and "small moon" and => account for small circle */
  147. int ii = (i + 1) % N;
  148. t[1] = x[i];
  149. t[2] = x[ii];
  150. double sc=scalarprod(crossprod(t[1] - t[0], t[2] - t[0]), t[0]) ;
  151. assert(sc >= -1e-10); // Error: tri a l'env (wrong orientation)
  152. double area_gc = triarea(t[0], t[1], t[2]);
  153. double area_sc_gc_moon = 0;
  154. if (d[i]) /* handle small circle case */
  155. {
  156. Coord m = midpoint(t[1], t[2]);
  157. double mext = scalarprod(m, c[i]) - d[i];
  158. char sgl = (mext > 0) ? -1 : 1;
  159. area_sc_gc_moon = sgl * alun(arcdist(t[1], t[2]), fabs(scalarprod(t[1], pole)));
  160. gg_exact = gg_exact + sc_gc_moon_normalintegral(t[1], t[2], pole);
  161. }
  162. area += area_gc + area_sc_gc_moon; /* for "spherical circle segment" sum triangular part (at) and "small moon" and => account for small circle */
  163. g[i] = barycentre(t, 3) * (area_gc + area_sc_gc_moon);
  164. }
  165. gg = barycentre(g, N);
  166. gg_exact = proj(gg_exact);
  167. delete[] g;
  168. gg = gg_exact;
  169. return area;
  170. }
  171. double polygonarea(Coord *vertices, int N)
  172. {
  173. assert(N >= 3);
  174. /* compute polygon area as sum of triangles */
  175. Coord centre = barycentre(vertices, N);
  176. double area = 0;
  177. for (int i = 0; i < N; i++)
  178. area += triarea(centre, vertices[i], vertices[(i+1)%N]);
  179. return area;
  180. }
  181. int packedPolygonSize(const Elt& e)
  182. {
  183. return sizeof(e.id) + sizeof(e.src_id) + sizeof(e.x) + sizeof(e.val) +
  184. sizeof(e.n) + e.n*(sizeof(double)+sizeof(Coord));
  185. }
  186. void packPolygon(const Elt& e, char *buffer, int& pos)
  187. {
  188. *((GloId *) &(buffer[pos])) = e.id;
  189. pos += sizeof(e.id);
  190. *((GloId *) &(buffer[pos])) = e.src_id;
  191. pos += sizeof(e.src_id);
  192. *((Coord *) &(buffer[pos])) = e.x;
  193. pos += sizeof(e.x);
  194. *((double*) &(buffer[pos])) = e.val;
  195. pos += sizeof(e.val);
  196. *((int *) & (buffer[pos])) = e.n;
  197. pos += sizeof(e.n);
  198. for (int i = 0; i < e.n; i++)
  199. {
  200. *((double *) & (buffer[pos])) = e.d[i];
  201. pos += sizeof(e.d[i]);
  202. *((Coord *) &(buffer[pos])) = e.vertex[i];
  203. pos += sizeof(e.vertex[i]);
  204. }
  205. }
  206. void unpackPolygon(Elt& e, const char *buffer, int& pos)
  207. {
  208. e.id = *((GloId *) &(buffer[pos]));
  209. pos += sizeof(e.id);
  210. e.src_id = *((GloId *) &(buffer[pos]));
  211. pos += sizeof(e.src_id);
  212. e.x = *((Coord *) & (buffer[pos]) );
  213. pos += sizeof(e.x);
  214. e.val = *((double *) & (buffer[pos]));
  215. pos += sizeof(double);
  216. e.n = *((int *) & (buffer[pos]));
  217. pos += sizeof(int);
  218. for (int i = 0; i < e.n; i++)
  219. {
  220. e.d[i] = *((double *) & (buffer[pos]));
  221. pos += sizeof(double);
  222. e.vertex[i] = *((Coord *) & (buffer[pos]));
  223. pos += sizeof(Coord);
  224. }
  225. }
  226. int packIntersectionSize(const Elt& elt)
  227. {
  228. return elt.is.size() * (2*sizeof(int)+ sizeof(GloId) + 5*sizeof(double));
  229. }
  230. void packIntersection(const Elt& e, char* buffer,int& pos)
  231. {
  232. for (list<Polyg *>::const_iterator it = e.is.begin(); it != e.is.end(); ++it)
  233. {
  234. *((int *) &(buffer[0])) += 1;
  235. *((int *) &(buffer[pos])) = e.id.ind;
  236. pos += sizeof(int);
  237. *((double *) &(buffer[pos])) = e.area;
  238. pos += sizeof(double);
  239. *((GloId *) &(buffer[pos])) = (*it)->id;
  240. pos += sizeof(GloId);
  241. *((int *) &(buffer[pos])) = (*it)->n;
  242. pos += sizeof(int);
  243. *((double *) &(buffer[pos])) = (*it)->area;
  244. pos += sizeof(double);
  245. *((Coord *) &(buffer[pos])) = (*it)->x;
  246. pos += sizeof(Coord);
  247. }
  248. }
  249. void unpackIntersection(Elt* e, const char* buffer)
  250. {
  251. int ind;
  252. int pos = 0;
  253. int n = *((int *) & (buffer[pos]));
  254. pos += sizeof(int);
  255. for (int i = 0; i < n; i++)
  256. {
  257. ind = *((int*) &(buffer[pos]));
  258. pos+=sizeof(int);
  259. Elt& elt= e[ind];
  260. elt.area=*((double *) & (buffer[pos]));
  261. pos += sizeof(double);
  262. Polyg *polygon = new Polyg;
  263. polygon->id = *((GloId *) & (buffer[pos]));
  264. pos += sizeof(GloId);
  265. polygon->n = *((int *) & (buffer[pos]));
  266. pos += sizeof(int);
  267. polygon->area = *((double *) & (buffer[pos]));
  268. pos += sizeof(double);
  269. polygon->x = *((Coord *) & (buffer[pos]));
  270. pos += sizeof(Coord);
  271. elt.is.push_back(polygon);
  272. }
  273. }
  274. }