libacfutils
A general purpose library of utility functions designed to make it easier to develop addons for the X-Plane flight simulator.
Loading...
Searching...
No Matches
geom.c
1/*
2 * CDDL HEADER START
3 *
4 * This file and its contents are supplied under the terms of the
5 * Common Development and Distribution License ("CDDL"), version 1.0.
6 * You may only use this file in accordance with the terms of version
7 * 1.0 of the CDDL.
8 *
9 * A full copy of the text of the CDDL should have accompanied this
10 * source. A copy of the CDDL is also available via the Internet at
11 * http://www.illumos.org/license/CDDL.
12 *
13 * CDDL HEADER END
14 */
15/*
16 * Copyright 2023 Saso Kiselkov. All rights reserved.
17 */
18
19#include <math.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdio.h>
23
24#include "acfutils/assert.h"
25#include "acfutils/math.h"
26#include "acfutils/helpers.h"
27#include "acfutils/geom.h"
28#include "acfutils/perf.h"
29#include "acfutils/safe_alloc.h"
30
31#define ROUND_ERROR 1e-10
32
38const ellip_t wgs84 = {
39 .a = 6378137.0,
40 .b = 6356752.314245,
41 .f = .00335281066474748071,
42 .ecc = 0.08181919084296430238,
43 .ecc2 = 0.00669437999019741354,
44 .r = 6371200.0
45};
46
47/*
48 * Naive implementation of matrix multiplication. We don't use this very
49 * heavily and can thus afford to rely on compiler auto-vectorization to
50 * get this optimized.
51 * Multiplies `x' and `y' and places result in 'z'. `xrows', `ycols' and `sz'
52 * have meanings as explained below:
53 * sz ycols ycols
54 * | | | | | |
55 * | | | | | |
56 *
57 * --- ++= =++ .-++= =++ ++= =++ --
58 * xrows ^ || x00 x01 || \/ / || y00 y01 || --- || z00 z01 || ^ xrows
59 * v || x10 x11 || /\ / || y10 y11 || --- || z10 z11 || v
60 * --- ++= =++ / .-++= =++ ++= =++ --
61 * --' /
62 * sz ^ /
63 * v /
64 * --'
65 */
66static void
67matrix_mul(const double *x, const double *y, double *z,
68 size_t xrows, size_t ycols, size_t sz)
69{
70 memset(z, 0, sz * ycols * sizeof (double));
71 for (size_t row = 0; row < xrows; row++) {
72 for (size_t col = 0; col < ycols; col++) {
73 for (size_t i = 0; i < sz; i++) {
74 z[row * ycols + col] += x[row * sz + i] *
75 y[i * ycols + col];
76 }
77 }
78 }
79}
80
92bool_t
93is_on_arc(double angle_x, double angle1, double angle2, bool_t cw)
94{
95 if (cw) {
96 if (angle1 < angle2)
97 return (angle_x >= angle1 && angle_x <= angle2);
98 else
99 return (angle_x >= angle1 || angle_x <= angle2);
100 } else {
101 if (angle1 < angle2)
102 return (angle_x <= angle1 || angle_x >= angle2);
103 else
104 return (angle_x <= angle1 && angle_x >= angle2);
105 }
106}
107
114double
116{
117 return (sqrt(POW2(a.x) + POW2(a.y) + POW2(a.z)));
118}
119
123long double
125{
126 return (sqrtl(POW2(a.x) + POW2(a.y) + POW2(a.z)));
127}
128
132double
134{
135 return (vect3_abs(vect3_sub(a, b)));
136}
137
141long double
143{
144 return (vect3l_abs(vect3l_sub(a, b)));
145}
146
150double
152{
153 return (sqrt(POW2(a.x) + POW2(a.y)));
154}
155
164{
165 return (VECT3(a.x * b.x, a.y * b.y, a.z * b.z));
166}
167
173{
174 return (VECT3L(a.x * b.x, a.y * b.y, a.z * b.z));
175}
176
182{
183 return (VECT2(a.x * b.x, a.y * b.y));
184}
185
189double
191{
192 return (vect2_abs(vect2_sub(a, b)));
193}
194
201vect3_set_abs(vect3_t a, double abs)
202{
203 double oldval = vect3_abs(a);
204 if (oldval != 0.0)
205 return (vect3_scmul(a, abs / oldval));
206 else
207 return (ZERO_VECT3);
208}
209
214vect3l_set_abs(vect3l_t a, long double abs)
215{
216 long double oldval = vect3l_abs(a);
217 if (oldval != 0.0)
218 return (vect3l_scmul(a, abs / oldval));
219 else
220 return (ZERO_VECT3L);
221}
222
227vect2_set_abs(vect2_t a, double abs)
228{
229 double oldval = vect2_abs(a);
230 if (oldval != 0.0)
231 return (vect2_scmul(a, abs / oldval));
232 else
233 return (ZERO_VECT2);
234}
235
242vect3_unit(vect3_t a, double *l)
243{
244 double len = vect3_abs(a);
245 /* Always be sure to give the caller the length, even if it's zero! */
246 if (l != NULL)
247 *l = len;
248 if (len == 0)
249 return (NULL_VECT3);
250 return (VECT3(a.x / len, a.y / len, a.z / len));
251}
252
257vect3l_unit(vect3l_t a, long double *l)
258{
259 long double len = vect3l_abs(a);
260 /* Always be sure to give the caller the length, even if it's zero! */
261 if (l != NULL)
262 *l = len;
263 if (len == 0)
264 return (NULL_VECT3L);
265 return (VECT3L(a.x / len, a.y / len, a.z / len));
266}
267
272vect2_unit(vect2_t a, double *l)
273{
274 double len;
275 len = vect2_abs(a);
276 /* Always be sure to give the caller the length, even if it's zero! */
277 if (l != NULL)
278 *l = len;
279 if (len == 0)
280 return (NULL_VECT2);
281 return (VECT2(a.x / len, a.y / len));
282}
283
293{
294 return (VECT3(a.x + b.x, a.y + b.y, a.z + b.z));
295}
296
302{
303 return (VECT3L(a.x + b.x, a.y + b.y, a.z + b.z));
304}
305
311{
312 return (VECT2(a.x + b.x, a.y + b.y));
313}
314
324{
325 return (VECT3(a.x - b.x, a.y - b.y, a.z - b.z));
326}
327
333{
334 return (VECT3L(a.x - b.x, a.y - b.y, a.z - b.z));
335}
336
342{
343 return (VECT2(a.x - b.x, a.y - b.y));
344}
345
356{
357 return (VECT3(a.x * b, a.y * b, a.z * b));
358}
359
364vect3l_scmul(vect3l_t a, long double b)
365{
366 return (VECT3L(a.x * b, a.y * b, a.z * b));
367}
368
374{
375 return (VECT2(a.x * b, a.y * b));
376}
377
385double
387{
388 return (a.x * b.x + a.y * b.y + a.z * b.z);
389}
390
394long double
396{
397 return (a.x * b.x + a.y * b.y + a.z * b.z);
398}
399
403double
405{
406 return (a.x * b.x + a.y * b.y);
407}
408
418{
419 return (VECT3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
420 a.x * b.y - a.y * b.x));
421}
422
428{
429 return (VECT3L(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
430 a.x * b.y - a.y * b.x));
431}
432
449{
450 return (VECT3((a.x + b.x) / 2, (a.y + b.y) / 2, (a.z + b.z) / 2));
451}
452
458{
459 return (VECT3L((a.x + b.x) / 2, (a.y + b.y) / 2, (a.z + b.z) / 2));
460}
461
467{
468 return (VECT2((a.x + b.x) / 2, (a.y + b.y) / 2));
469}
470
479vect3_rot(vect3_t v, double a, unsigned axis)
480{
481 ASSERT3U(axis, <=, 2);
482 switch (axis) {
483 case 0: {
484 double sin_a = sin(DEG2RAD(-a)), cos_a = cos(DEG2RAD(-a));
485 return (VECT3(v.x, v.y * cos_a - v.z * sin_a,
486 v.y * sin_a + v.z * cos_a));
487 }
488 case 1: {
489 double sin_a = sin(DEG2RAD(a)), cos_a = cos(DEG2RAD(a));
490 return (VECT3(v.x * cos_a - v.z * sin_a, v.y,
491 v.x * sin_a + v.z * cos_a));
492 }
493 case 2: {
494 double sin_a = sin(DEG2RAD(-a)), cos_a = cos(DEG2RAD(-a));
495 return (VECT3(v.x * cos_a - v.y * sin_a,
496 v.x * sin_a + v.y * cos_a, v.z));
497 }
498 default:
499 VERIFY_MSG(0, "Invalid axis value (%d) passed", axis);
500 }
501}
502
507vect3l_rot(vect3l_t v, long double a, unsigned axis)
508{
509 ASSERT3U(axis, <=, 2);
510 switch (axis) {
511 case 0: {
512 double sin_a = sinl(DEG2RAD(-a)), cos_a = cosl(DEG2RAD(-a));
513 return (VECT3L(v.x, v.y * cos_a - v.z * sin_a,
514 v.y * sin_a + v.z * cos_a));
515 }
516 case 1: {
517 double sin_a = sinl(DEG2RAD(a)), cos_a = cosl(DEG2RAD(a));
518 return (VECT3L(v.x * cos_a - v.z * sin_a, v.y,
519 v.x * sin_a + v.z * cos_a));
520 }
521 case 2: {
522 double sin_a = sinl(DEG2RAD(-a)), cos_a = cosl(DEG2RAD(-a));
523 return (VECT3L(v.x * cos_a - v.y * sin_a,
524 v.x * sin_a + v.y * cos_a, v.z));
525 }
526 default:
527 VERIFY_MSG(0, "Invalid axis value (%d) passed", axis);
528 }
529}
530
536vect2_norm(vect2_t v, bool_t right)
537{
538 if (right)
539 return (VECT2(v.y, -v.x));
540 else
541 return (VECT2(-v.y, v.x));
542}
543
548vect2_rot(vect2_t v, double a)
549{
550 double sin_a = sin(DEG2RAD(-a)), cos_a = cos(DEG2RAD(-a));
551 return (VECT2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a));
552}
553
560{
561 double sin_a = sin(DEG2RAD(-a)), cos_a = cos(DEG2RAD(-a));
562 return (VECT2(v.x * cos_a + v.y * sin_a, v.y * cos_a - v.x * sin_a));
563}
564
570{
571 return (VECT3(-v.x, -v.y, -v.z));
572}
573
579{
580 return (VECT3L(-v.x, -v.y, -v.z));
581}
582
588{
589 return (VECT2(-v.x, -v.y));
590}
591
600vect3_local2acf(vect3_t v, double roll, double pitch, double hdgt)
601{
602 return (vect3_rot(vect3_rot(vect3_rot(v, -hdgt, 1), pitch, 0),
603 -roll, 2));
604}
605
610vect3_acf2local(vect3_t v, double roll, double pitch, double hdgt)
611{
612 return (vect3_rot(vect3_rot(vect3_rot(v, roll, 2), -pitch, 0),
613 hdgt, 1));
614}
615
622double
623rel_angle(double a1, double a2)
624{
625 ASSERT(isfinite(a1));
626 ASSERT(isfinite(a2));
627 a1 = fmod(a1, 360);
628 if (a1 < 0.0)
629 a1 += 360.0;
630 a2 = fmod(a2, 360);
631 if (a2 < 0.0)
632 a2 += 360.0;
633 if (a1 > a2) {
634 if (a1 > a2 + 180)
635 return (360 - a1 + a2);
636 else
637 return (-(a1 - a2));
638 } else {
639 if (a2 > a1 + 180)
640 return (-(360 - a2 + a1));
641 else
642 return (a2 - a1);
643 }
644}
645
664{
665 vect3_t result;
666 double lat_rad, lon_rad, R, R0;
667
668 lat_rad = DEG2RAD(pos.lat);
669 lon_rad = DEG2RAD(pos.lon);
670
671 /* R is total distance from center at alt_msl */
672 R = pos.elev + EARTH_MSL;
673 /*
674 * R0 is the radius of a circular cut parallel to the equator at the
675 * given latitude of a sphere with radius R.
676 */
677 R0 = R * cos(lat_rad);
678 /* Given R and R0, we can transform the geo coords into 3-space: */
679 result.x = R0 * cos(lon_rad);
680 result.y = R0 * sin(lon_rad);
681 result.z = R * sin(lat_rad);
682
683 return (result);
684}
685
695geo2ecmi(geo_pos3_t pos, double delta_t, const ellip_t *ellip)
696{
697 ASSERT(!IS_NULL_GEO_POS(pos));
698 ASSERT(!isnan(delta_t));
699 ASSERT(ellip != NULL);
700 return (ecef2ecmi(geo2ecef_mtr(pos, ellip), delta_t));
701}
702
712ecmi2geo(vect3_t pos, double delta_t, const ellip_t *ellip)
713{
714 ASSERT(!IS_NULL_VECT(pos));
715 ASSERT(!isnan(delta_t));
716 ASSERT(ellip != NULL);
717 return (ecef2geo(ecmi2ecef(pos, delta_t), ellip));
718}
719
725sph2ecmi(geo_pos3_t pos, double delta_t)
726{
727 ASSERT(!IS_NULL_GEO_POS(pos));
728 ASSERT(!isnan(delta_t));
729 return (ecef2ecmi(sph2ecef(pos), delta_t));
730}
731
737ecmi2sph(vect3_t pos, double delta_t)
738{
739 ASSERT(!IS_NULL_VECT(pos));
740 ASSERT(!isnan(delta_t));
741 return (ecef2sph(ecmi2ecef(pos, delta_t)));
742}
743
749ecef2ecmi(vect3_t pos, double delta_t)
750{
751 ASSERT(!IS_NULL_VECT(pos));
752 ASSERT(!isnan(delta_t));
753 return (vect3_rot(pos, -delta_t * EARTH_ROT_RATE, 2));
754}
755
761ecmi2ecef(vect3_t pos, double delta_t)
762{
763 ASSERT(!IS_NULL_VECT(pos));
764 ASSERT(!isnan(delta_t));
765 return (vect3_rot(pos, delta_t * EARTH_ROT_RATE, 2));
766}
767
773{
774 return (VECT3(ecef.y, ecef.z, ecef.x));
775}
776
782{
783 return (VECT3(opengl.z, opengl.x, opengl.y));
784}
785
791{
792 return (VECT3L(ecef.y, ecef.z, ecef.x));
793}
794
800{
801 return (VECT3L(opengl.z, opengl.x, opengl.y));
802}
803
811ellip_init(double semi_major, double semi_minor, double flattening)
812{
813 ellip_t ellip;
814
815 ellip.a = semi_major;
816 ellip.b = semi_minor;
817 ellip.ecc2 = flattening * (2 - flattening);
818
819 return (ellip);
820}
821
831geo2sph(geo_pos3_t pos, const ellip_t *ellip)
832{
833 double lat_r = DEG2RAD(pos.lat);
834 double sin_lat = sin(lat_r);
835 double p, z;
836 double Rc; /* curvature of the prime vertical */
837 geo_pos3_t res;
838
839 Rc = ellip->a / sqrt(1 - ellip->ecc2 * POW2(sin_lat));
840 p = (Rc + pos.elev) * cos(lat_r);
841 z = ((Rc * (1 - ellip->ecc2)) + pos.elev) * sin_lat;
842
843 res.elev = sqrt(POW2(p) + POW2(z));
844 res.lat = RAD2DEG(asin(z / res.elev));
845 res.lon = pos.lon;
846
847 return (res);
848}
849
862{
863 double lat_r = DEG2RAD(pos.lat);
864 double lon_r = DEG2RAD(pos.lon);
865 double Rc; /* curvature of the prime vertical */
866 vect3_t res;
867 double sin_lat = sin(lat_r), cos_lat = cos(lat_r);
868 double sin_lon = sin(lon_r), cos_lon = cos(lon_r);
869
870 Rc = ellip->a / sqrt(1 - ellip->ecc2 * POW2(sin_lat));
871
872 res.x = (Rc + pos.elev) * cos_lat * cos_lon;
873 res.y = (Rc + pos.elev) * cos_lat * sin_lon;
874 res.z = (Rc * (1 - ellip->ecc2) + pos.elev) * sin_lat;
875
876 return (res);
877}
878
885{
886 return (geo2ecef_mtr(GEO_POS3(pos.lat, pos.lon, FEET2MET(pos.elev)),
887 ellip));
888}
889
900ecef2geo(vect3_t pos, const ellip_t *ellip)
901{
902 geo_pos3_t res;
903 double B;
904 double d;
905 double e;
906 double f;
907 double g;
908 double p;
909 double q;
910 double r;
911 double t;
912 double v;
913 double zlong;
914
915 /*
916 * 1.0 compute semi-minor axis and set sign to that of z in order
917 * to get sign of Phi correct.
918 */
919 B = (pos.z >= 0 ? ellip->b : -ellip->b);
920
921 /*
922 * 2.0 compute intermediate values for latitude
923 */
924 r = sqrt(POW2(pos.x) + POW2(pos.y));
925 e = (B * pos.z - (POW2(ellip->a) - POW2(B))) / (ellip->a * r);
926 f = (B * pos.z + (POW2(ellip->a) - POW2(B))) / (ellip->a * r);
927
928 /*
929 * 3.0 find solution to:
930 * t^4 + 2*E*t^3 + 2*F*t - 1 = 0
931 */
932 p = (4.0 / 3.0) * (e * f + 1.0);
933 q = 2.0 * (POW2(e) - POW2(f));
934 d = POW3(p) + POW2(q);
935
936 if (d >= 0.0) {
937 v = pow((sqrt(d) - q), (1.0 / 3.0)) - pow((sqrt(d) + q),
938 (1.0 / 3.0));
939 } else {
940 v = 2.0 * sqrt(-p) * cos(acos(q / (p * sqrt(-p))) / 3.0);
941 }
942
943 /*
944 * 4.0 improve v
945 * NOTE: not really necessary unless point is near pole
946 */
947 if (POW2(v) < fabs(p))
948 v = -(POW3(v) + 2.0 * q) / (3.0 * p);
949 g = (sqrt(POW2(e) + v) + e) / 2.0;
950 t = sqrt(POW2(g) + (f - v * g) / (2.0 * g - e)) - g;
951
952 res.lat = atan((ellip->a * (1.0 - POW2(t))) / (2.0 * B * t));
953
954 /*
955 * 5.0 compute height above ellipsoid
956 */
957 res.elev = (r - ellip->a * t) * cos(res.lat) +
958 (pos.z - B) * sin(res.lat);
959
960 /*
961 * 6.0 compute longitude east of Greenwich
962 */
963 zlong = atan2(pos.y, pos.x);
964 if (zlong < 0.0)
965 zlong = zlong + (2 * M_PI);
966
967 res.lon = zlong;
968
969 /*
970 * 7.0 convert latitude and longitude to degrees (elev is in meters).
971 */
972 res.lat = RAD2DEG(res.lat);
973 res.lon = RAD2DEG(res.lon);
974 if (res.lon >= 180.0)
975 res.lon -= 360.0;
976
977 return (res);
978}
979
988{
989 geo_pos3_t pos;
990 double lat_rad, lon_rad, R, R0;
991
992 R0 = sqrt(v.x * v.x + v.y * v.y);
993 R = vect3_abs(v);
994 if (R0 == 0) {
995 /* to prevent a div-by-zero at the poles */
996 R0 = 0.000000001;
997 }
998 lat_rad = atan(v.z / R0);
999 lon_rad = asin(v.y / R0);
1000 if (v.x < 0.0) {
1001 if (v.y >= 0.0)
1002 lon_rad = M_PI - lon_rad;
1003 else
1004 lon_rad = -M_PI - lon_rad;
1005 }
1006 pos.elev = R - EARTH_MSL;
1007 pos.lat = RAD2DEG(lat_rad);
1008 pos.lon = RAD2DEG(lon_rad);
1009
1010 return (pos);
1011}
1012
1034unsigned
1035vect2sph_isect(vect3_t v, vect3_t o, vect3_t c, double r, bool_t confined,
1036 vect3_t i[2])
1037{
1038 vect3_t l, o_min_c;
1039 double d, l_dot_o_min_c, sqrt_tmp, o_min_c_abs;
1040
1041 /* convert v into a unit vector 'l' and scalar distance 'd' */
1042 l = vect3_unit(v, &d);
1043
1044 /* compute (o - c) and the dot product of l.(o - c) */
1045 o_min_c = vect3_sub(o, c);
1046 l_dot_o_min_c = vect3_dotprod(l, o_min_c);
1047
1048 /*
1049 * The full formula for the distance along L for the intersects is:
1050 * -(l.(o - c)) +- sqrt((l.(o - c))^2 - abs(o - c)^2 + r^2)
1051 * The part in the sqrt may be negative, zero or positive, indicating
1052 * respectively no intersection, one touching point or two points, so
1053 * before we start sqrt()ing away, we check which it is. Also, this
1054 * checks for a solution on an infinite line between extending along
1055 * v. Before we declare victory, we check that the computed
1056 * points lie on the vector.
1057 */
1058 o_min_c_abs = vect3_abs(o_min_c);
1059 sqrt_tmp = POW2(l_dot_o_min_c) - POW2(o_min_c_abs) + POW2(r);
1060 if (sqrt_tmp > 0) {
1061 /* Two solutions */
1062 double i1_d, i2_d;
1063 unsigned intersects = 0;
1064
1065 sqrt_tmp = sqrt(sqrt_tmp);
1066
1067 i1_d = -l_dot_o_min_c - sqrt_tmp;
1068 if ((i1_d >= 0 && i1_d <= d) || !confined) {
1069 /*
1070 * Solution lies on vector, store a vector to it
1071 * if the caller requested it.
1072 */
1073 if (i != NULL)
1074 i[intersects] = vect3_add(vect3_scmul(l, i1_d),
1075 o);
1076 intersects++;
1077 } else {
1078 /* Solution lies outside of line between o1 & o2 */
1079 i1_d = NAN;
1080 if (i != NULL)
1081 i[intersects] = NULL_VECT3;
1082 }
1083
1084 /* ditto for the second intersect */
1085 i2_d = -l_dot_o_min_c + sqrt_tmp;
1086 if ((i2_d >= 0 && i2_d <= d) || !confined) {
1087 if (i != NULL)
1088 i[intersects] = vect3_add(vect3_scmul(l, i2_d),
1089 o);
1090 intersects++;
1091 } else {
1092 i2_d = NAN;
1093 if (i != NULL)
1094 i[intersects] = NULL_VECT3;
1095 }
1096
1097 return (intersects);
1098 } else if (sqrt_tmp == 0) {
1099 /* One solution */
1100 double i1_d;
1101
1102 if (i != NULL)
1103 i[1] = NULL_VECT3;
1104
1105 i1_d = -l_dot_o_min_c;
1106 if ((i1_d >= 0 && i1_d <= d) || !confined) {
1107 if (i != NULL)
1108 i[0] = vect3_add(vect3_scmul(l, i1_d), o);
1109 return (1);
1110 } else {
1111 if (i != NULL)
1112 i[0] = NULL_VECT3;
1113 return (0);
1114 }
1115 } else {
1116 /* No solution, no intersections, NaN i1 & i2 */
1117 if (i != NULL) {
1118 i[0] = NULL_VECT3;
1119 i[1] = NULL_VECT3;
1120 }
1121 return (0);
1122 }
1123}
1124
1130unsigned
1131vect2circ_isect(vect2_t v, vect2_t o, vect2_t c, double r, bool_t confined,
1132 vect2_t i[2])
1133{
1134 /*
1135 * This is basically a simplified case of a vect2sph intersection,
1136 * where both the vector and sphere's center lie on the xy plane.
1137 * So just convert to 3D coordinates with z=0 and run vect2sph_isect.
1138 * This only adds one extra coordinate to the calculation, which is
1139 * generally negligible on performance.
1140 */
1141 vect3_t v3 = VECT3(v.x, v.y, 0), o3 = VECT3(o.x, o.y, 0);
1142 vect3_t c3 = VECT3(c.x, c.y, 0);
1143 vect3_t i3[2];
1144 int n;
1145
1146 n = vect2sph_isect(v3, o3, c3, r, confined, i3);
1147 if (i != NULL) {
1148 i[0] = VECT2(i3[0].x, i3[0].y);
1149 i[1] = VECT2(i3[1].x, i3[1].y);
1150 }
1151
1152 return (n);
1153}
1154
1170vect2_t
1171vect2vect_isect(vect2_t a, vect2_t oa, vect2_t b, vect2_t ob, bool_t confined)
1172{
1173 vect2_t p1, p2, p3, p4, r;
1174 double ca, cb, det;
1175
1176 if (VECT2_EQ(oa, ob))
1177 return (oa);
1178
1179 p1 = oa;
1180 p2 = vect2_add(oa, a);
1181 p3 = ob;
1182 p4 = vect2_add(ob, b);
1183
1184 det = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
1185 if (det == 0.0)
1186 return (NULL_VECT2);
1187 ca = p1.x * p2.y - p1.y * p2.x;
1188 cb = p3.x * p4.y - p3.y * p4.x;
1189 r.x = (ca * (p3.x - p4.x) - cb * (p1.x - p2.x)) / det;
1190 r.y = (ca * (p3.y - p4.y) - cb * (p1.y - p2.y)) / det;
1191
1192 if (confined) {
1193 if (r.x < MIN(p1.x, p2.x) - ROUND_ERROR ||
1194 r.x > MAX(p1.x, p2.x) + ROUND_ERROR ||
1195 r.x < MIN(p3.x, p4.x) - ROUND_ERROR ||
1196 r.x > MAX(p3.x, p4.x) + ROUND_ERROR ||
1197 r.y < MIN(p1.y, p2.y) - ROUND_ERROR ||
1198 r.y > MAX(p1.y, p2.y) + ROUND_ERROR ||
1199 r.y < MIN(p3.y, p4.y) - ROUND_ERROR ||
1200 r.y > MAX(p3.y, p4.y) + ROUND_ERROR)
1201 return (NULL_VECT2);
1202 }
1203
1204 return (r);
1205}
1206
1219unsigned
1220circ2circ_isect(vect2_t ca, double ra, vect2_t cb, double rb, vect2_t i[2])
1221{
1222 double a, d, h;
1223 vect2_t ca_cb, p2, ca_p2;
1224
1225 if (VECT2_EQ(ca, cb) && ra == rb)
1226 return (UINT_MAX);
1227
1228 ca_cb = vect2_sub(cb, ca);
1229 d = vect2_abs(ca_cb);
1230 if ((d == 0 && ra == rb) || d > ra + rb ||
1231 d + MIN(ra, rb) < MAX(ra, rb))
1232 return (0);
1233 a = (POW2(ra) - POW2(rb) + POW2(d)) / (2 * d);
1234 if (POW2(ra) - POW2(a) < 0)
1235 h = 0;
1236 else
1237 h = sqrt(POW2(ra) - POW2(a));
1238 ca_p2 = vect2_set_abs(ca_cb, a);
1239 p2 = vect2_add(ca, ca_p2);
1240
1241 if (h == 0) {
1242 i[0] = p2;
1243 ASSERT(!IS_NULL_VECT(i[0]));
1244 return (1);
1245 } else {
1246 i[0] = vect2_add(p2, vect2_set_abs(vect2_norm(ca_p2, B_FALSE),
1247 h));
1248 i[1] = vect2_add(p2, vect2_set_abs(vect2_norm(ca_p2, B_TRUE),
1249 h));
1250 ASSERT(!IS_NULL_VECT(i[0]) && !IS_NULL_VECT(i[1]));
1251 return (2);
1252 }
1253}
1254
1255UNUSED_ATTR static bool_t
1256is_valid_poly(const vect2_t *poly)
1257{
1258 /* A polygon must contain at least 3 points */
1259 return (poly != NULL && !IS_NULL_VECT(poly[0]) &&
1260 !IS_NULL_VECT(poly[1]) && !IS_NULL_VECT(poly[2]));
1261}
1262
1263static unsigned
1264get_poly_num_pts(const vect2_t *poly)
1265{
1266 for (unsigned i = 0; ; i++) {
1267 if (IS_NULL_VECT(poly[i]))
1268 return (i);
1269 }
1270}
1271
1290unsigned
1292 vect2_t *isects, unsigned cap)
1293{
1294 unsigned n_isects = 0;
1295 unsigned npts;
1296
1297 ASSERT(is_valid_poly(poly));
1298 npts = get_poly_num_pts(poly);
1299
1300 for (size_t i = 0; i < npts; i++) {
1301 vect2_t pt1 = poly[i];
1302 vect2_t pt2 = poly[(i + 1) % npts];
1303 vect2_t v = vect2_sub(pt2, pt1);
1304 vect2_t isect = vect2vect_isect(a, oa, v, pt1, B_TRUE);
1305 if (!IS_NULL_VECT(isect)) {
1306 n_isects++;
1307 if (cap != 0) {
1308 ASSERT(isects != NULL);
1309 *isects = isect;
1310 isects++;
1311 cap--;
1312 }
1313 }
1314 }
1315
1316 return (n_isects);
1317}
1318
1325unsigned
1327{
1328 return (vect2poly_isect_get(a, oa, poly, NULL, 0));
1329}
1330
1339bool_t
1341{
1342 ASSERT(is_valid_poly(poly));
1343 /*
1344 * The simplest approach is ray casting. Construct a vector from `pt'
1345 * to a point very far away and count how many edges of the polygon
1346 * we hit. If we hit an even number, we're outside, otherwise we're
1347 * inside.
1348 */
1349 vect2_t v = vect2_sub(VECT2(1e20, 1e20), pt);
1350 unsigned isects = vect2poly_isect(v, pt, poly);
1351 return ((isects & 1) != 0);
1352}
1353
1358vect2_t
1359hdg2dir(double truehdg)
1360{
1361 truehdg = DEG2RAD(truehdg);
1362 return (VECT2(sin(truehdg), cos(truehdg)));
1363}
1364
1370double
1372{
1373 double res;
1374
1375 if (dir.x >= 0 && dir.y >= 0)
1376 res = RAD2DEG(asin(dir.x / vect2_abs(dir)));
1377 else if (dir.x < 0 && dir.y >= 0)
1378 res = 360 + RAD2DEG(asin(dir.x / vect2_abs(dir)));
1379 else if (dir.x >= 0 && dir.y < 0)
1380 res = 180 - RAD2DEG(asin(dir.x / vect2_abs(dir)));
1381 else
1382 res = 180 - RAD2DEG(asin(dir.x / vect2_abs(dir)));
1383
1384 /*
1385 * The result might VERY subtly be off by just a fraction from a normal
1386 * heading, causing this to crash other assertion-checking functions.
1387 */
1388 return (normalize_hdg(res));
1389}
1390
1405geo_displace(const ellip_t *ellip, geo_pos2_t pos, double truehdg, double dist)
1406{
1407 return (geo_displace_dir(ellip, pos, hdg2dir(truehdg), dist));
1408}
1409
1417geo_displace_dir(const ellip_t *ellip, geo_pos2_t pos, vect2_t dir, double dist)
1418{
1419 double dist_r = dist / EARTH_MSL;
1420 fpp_t fpp;
1421
1422 if (dist >= M_PI * EARTH_MSL / 2)
1423 return (NULL_GEO_POS2);
1424 fpp = gnomo_fpp_init(pos, 0, ellip, B_TRUE);
1425 dir = vect2_set_abs(dir, EARTH_MSL * tan(dist_r));
1426
1427 return (fpp2geo(dir, &fpp));
1428}
1429
1441bool_t
1442geo_pos2_from_str(const char *lat, const char *lon, geo_pos2_t *pos)
1443{
1444 pos->lat = atof(lat);
1445 pos->lon = atof(lon);
1446 return (is_valid_lat(pos->lat) && is_valid_lon(pos->lon));
1447}
1448
1453bool_t
1454geo_pos3_from_str(const char *lat, const char *lon, const char *elev,
1455 geo_pos3_t *pos)
1456{
1457 pos->lat = atof(lat);
1458 pos->lon = atof(lon);
1459 pos->elev = atof(elev);
1460 return (is_valid_lat(pos->lat) && is_valid_lon(pos->lon) &&
1461 is_valid_elev(pos->elev));
1462}
1463
1464/* Cotangent */
1465static inline double
1466cot(double x)
1467{
1468 return (1.0 / tan(x));
1469}
1470
1471/* Secant */
1472static inline double
1473sec(double x)
1474{
1475 return (1.0 / cos(x));
1476}
1477
1498sph_xlate_init(geo_pos2_t displac, double rot, bool_t inv)
1499{
1500 /*
1501 * (ECEF axes:)
1502 * lat xlate y axis alpha
1503 * lon xlate z axis bravo
1504 * viewport rotation x axis theta
1505 */
1506 sph_xlate_t xlate;
1507 double alpha = DEG2RAD(!inv ? displac.lat : -displac.lat);
1508 double bravo = DEG2RAD(!inv ? -displac.lon : displac.lon);
1509 double theta = DEG2RAD(!inv ? rot : -rot);
1510
1511#define M(m, r, c) ((m)[(r) * 3 + (c)])
1512 double R_a[3 * 3], R_b[3 * 3];
1513 double sin_alpha = sin(alpha), cos_alpha = cos(alpha);
1514 double sin_bravo = sin(bravo), cos_bravo = cos(bravo);
1515 double sin_theta = sin(theta), cos_theta = cos(theta);
1516
1517 /*
1518 * +- -+
1519 * | cos(a) 0 sin(a) |
1520 * | 0 1 0 |
1521 * | -sin(a) 0 cos(a) |
1522 * +- -+
1523 */
1524 memset(R_a, 0, sizeof (R_a));
1525 M(R_a, 0, 0) = cos_alpha;
1526 M(R_a, 0, 2) = sin_alpha;
1527 M(R_a, 1, 1) = 1;
1528 M(R_a, 2, 0) = -sin_alpha;
1529 M(R_a, 2, 2) = cos_alpha;
1530
1531 /*
1532 * +- -+
1533 * | cos(g) -sin(g) 0 |
1534 * | sin(g) cos(g) 0 |
1535 * | 0 0 1 |
1536 * +- -+
1537 */
1538 memset(R_b, 0, sizeof (R_b));
1539 M(R_b, 0, 0) = cos_bravo;
1540 M(R_b, 0, 1) = -sin_bravo;
1541 M(R_b, 1, 0) = sin_bravo;
1542 M(R_b, 1, 1) = cos_bravo;
1543 M(R_b, 2, 2) = 1;
1544
1545 if (!inv)
1546 matrix_mul(R_a, R_b, xlate.sph_matrix, 3, 3, 3);
1547 else
1548 matrix_mul(R_b, R_a, xlate.sph_matrix, 3, 3, 3);
1549
1550 xlate.rot_matrix[0] = cos_theta;
1551 xlate.rot_matrix[1] = -sin_theta;
1552 xlate.rot_matrix[2] = sin_theta;
1553 xlate.rot_matrix[3] = cos_theta;
1554 xlate.inv = inv;
1555
1556 return (xlate);
1557}
1558
1564vect3_t
1566{
1567 vect3_t q;
1568 vect2_t r, s;
1569
1570 if (xlate->inv) {
1571 /* Undo projection plane rotation along y axis. */
1572 r = VECT2(p.y, p.z);
1573 matrix_mul(xlate->rot_matrix, (double *)&r, (double *)&s,
1574 2, 1, 2);
1575 p.y = s.x;
1576 p.z = s.y;
1577 }
1578
1579 matrix_mul(xlate->sph_matrix, (double *)&p, (double *)&q, 3, 1, 3);
1580
1581 if (!xlate->inv) {
1582 /*
1583 * In the final projection plane, grab y & z coords & rotate
1584 * along x axis.
1585 */
1586 r = VECT2(q.y, q.z);
1587 matrix_mul(xlate->rot_matrix, (double *)&r, (double *)&s,
1588 2, 1, 2);
1589 q.y = s.x;
1590 q.z = s.y;
1591 }
1592
1593 return (q);
1594}
1595
1603{
1604 vect3_t v = sph2ecef(GEO2_TO_GEO3(pos, 0));
1605 vect3_t r = sph_xlate_vect(v, xlate);
1606 geo_pos3_t res = ecef2sph(r);
1607 return (GEO_POS2(res.lat, res.lon));
1608}
1609
1614double
1616{
1617 /*
1618 * Convert both coordinates into 3D vectors and calculate the angle
1619 * between them. GC distance is proportional to that angle.
1620 */
1621 vect3_t start_v = geo2ecef_mtr(GEO2_TO_GEO3(start, 0), &wgs84);
1622 vect3_t end_v = geo2ecef_mtr(GEO2_TO_GEO3(end, 0), &wgs84);
1623 vect3_t s2e = vect3_sub(end_v, start_v);
1624 double s2e_abs = vect3_abs(s2e);
1625 double alpha = asin(MIN(s2e_abs / 2 / EARTH_MSL, 1.0));
1626 return (2 * alpha * EARTH_MSL);
1627}
1628
1636double
1638{
1639 fpp_t fpp = ortho_fpp_init(start, 0, &wgs84, B_FALSE);
1640 return (dir2hdg(geo2fpp(end, &fpp)));
1641}
1642
1679fpp_t
1680fpp_init(geo_pos2_t center, double rot, double dist, const ellip_t *ellip,
1681 bool_t allow_inv)
1682{
1683 fpp_t fpp;
1684
1685 VERIFY(dist != 0);
1686 fpp.allow_inv = allow_inv;
1687 fpp.ellip = ellip;
1688 if (ellip) {
1690 0), ellip));
1691 fpp.xlate = sph_xlate_init(sph_ctr, rot, B_FALSE);
1692 if (allow_inv)
1693 fpp.inv_xlate = sph_xlate_init(sph_ctr, rot, B_TRUE);
1694 } else {
1695 fpp.xlate = sph_xlate_init(center, rot, B_FALSE);
1696 if (allow_inv)
1697 fpp.inv_xlate = sph_xlate_init(center, rot, B_TRUE);
1698 }
1699 fpp.dist = dist;
1700 fpp.scale = VECT2(1, 1);
1701
1702 return (fpp);
1703}
1704
1710fpp_t
1711ortho_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip,
1712 bool_t allow_inv)
1713{
1714 return (fpp_init(center, rot, INFINITY, ellip, allow_inv));
1715}
1716
1722fpp_t
1723gnomo_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip,
1724 bool_t allow_inv)
1725{
1726 return (fpp_init(center, rot, -EARTH_MSL, ellip, allow_inv));
1727}
1728
1735fpp_t
1736stereo_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip,
1737 bool_t allow_inv)
1738{
1739 return (fpp_init(center, rot, -2 * EARTH_MSL, ellip, allow_inv));
1740}
1741
1748vect2_t
1749geo2fpp(geo_pos2_t pos, const fpp_t *fpp)
1750{
1751 vect3_t pos_v;
1752 vect2_t res_v;
1753
1754 if (fpp->ellip != NULL)
1755 pos_v = geo2ecef_mtr(GEO2_TO_GEO3(pos, 0), fpp->ellip);
1756 else
1757 pos_v = sph2ecef(GEO2_TO_GEO3(pos, 0));
1758 pos_v = sph_xlate_vect(pos_v, &fpp->xlate);
1759 if (isfinite(fpp->dist)) {
1760 if (fpp->dist < 0.0 && pos_v.x <= fpp->dist + EARTH_MSL)
1761 return (NULL_VECT2);
1762 res_v.x = fpp->dist * (pos_v.y / (fpp->dist +
1763 EARTH_MSL - pos_v.x));
1764 res_v.y = fpp->dist * (pos_v.z / (fpp->dist +
1765 EARTH_MSL - pos_v.x));
1766 } else {
1767 res_v = VECT2(pos_v.y, pos_v.z);
1768 }
1769
1770 return (VECT2(res_v.x * fpp->scale.x, res_v.y * fpp->scale.y));
1771}
1772
1787fpp2geo(vect2_t pos, const fpp_t *fpp)
1788{
1789 vect3_t v;
1790 vect3_t o;
1791 vect3_t i[2];
1792 vect3_t r;
1793 int n;
1794
1795 ASSERT(fpp->allow_inv);
1796 ASSERT(fpp->scale.x != 0);
1797 ASSERT(fpp->scale.y != 0);
1798
1799 pos = VECT2(pos.x / fpp->scale.x, pos.y / fpp->scale.y);
1800 if (isfinite(fpp->dist)) {
1801 v = VECT3(-fpp->dist, pos.x, pos.y);
1802 o = VECT3(EARTH_MSL + fpp->dist, 0, 0);
1803 } else {
1804 /*
1805 * For orthographic projections, we'll just pretend the
1806 * projection point is *really* far away so that the
1807 * error in the result is negligible.
1808 */
1809 v = VECT3(-1e14, pos.x, pos.y);
1810 o = VECT3(1e14, 0, 0);
1811 }
1812 n = vect2sph_isect(v, o, ZERO_VECT3, EARTH_MSL, B_FALSE, i);
1813 if (n == 0) {
1814 /* Invalid input point, not a member of projection */
1815 return (NULL_GEO_POS2);
1816 }
1817 if (n == 2 && isfinite(fpp->dist)) {
1818 /*
1819 * Two solutions on non-orthographic projections. Find the
1820 * one that's closer to the projection origin while located
1821 * between it and the projection plane and place it in i[0].
1822 */
1823 if (fpp->dist >= -2 * EARTH_MSL) {
1824 if (i[1].x > i[0].x)
1825 i[0] = i[1];
1826 } else {
1827 if (i[1].x < i[0].x)
1828 i[0] = i[1];
1829 }
1830 }
1831 /* Result is now in i[0]. Inv-xlate to global space & ecef2sph. */
1832 r = sph_xlate_vect(i[0], &fpp->inv_xlate);
1833 if (fpp->ellip != NULL)
1834 return (GEO3_TO_GEO2(ecef2geo(r, fpp->ellip)));
1835 else
1836 return (GEO3_TO_GEO2(ecef2sph(r)));
1837}
1838
1849void
1851{
1852 ASSERT(fpp != NULL);
1853 ASSERT(fpp->scale.x != 0);
1854 ASSERT(fpp->scale.y != 0);
1855 fpp->scale = scale;
1856}
1857
1862vect2_t
1864{
1865 ASSERT(fpp != NULL);
1866 return (fpp->scale);
1867}
1868
1874double
1876{
1877 ASSERT(fpp->allow_inv);
1878 return (gc_distance(fpp2geo(p1, fpp), fpp2geo(p2, fpp)));
1879}
1880
1891lcc_t
1892lcc_init(double reflat, double reflon, double stdpar1, double stdpar2)
1893{
1894 double phi0 = DEG2RAD(reflat);
1895 double phi1 = DEG2RAD(stdpar1);
1896 double phi2 = DEG2RAD(stdpar2);
1897 lcc_t lcc;
1898
1899 lcc.reflat = DEG2RAD(reflat);
1900 lcc.reflon = DEG2RAD(reflon);
1901
1902 if (stdpar1 == stdpar2)
1903 lcc.n = sin(phi1);
1904 else
1905 lcc.n = log(cos(phi1) * sec(phi2)) / log(tan(M_PI / 4.0 +
1906 phi2 / 2.0) * cot(M_PI / 4.0 + phi1 / 2.0));
1907 lcc.F = (cos(phi1) * pow(tan(M_PI / 4.0 * phi1 / 2.0), lcc.n)) /
1908 lcc.n;
1909 lcc.rho0 = lcc.F * pow(cot(M_PI / 4.0 + phi0 / 2.0), lcc.n);
1910
1911 return (lcc);
1912}
1913
1917vect2_t
1918geo2lcc(geo_pos2_t pos, const lcc_t *lcc)
1919{
1920 vect2_t result;
1921 double rho;
1922 double lat = DEG2RAD(pos.lat);
1923 double lon = DEG2RAD(pos.lon);
1924
1925 rho = lcc->F * pow(cot(M_PI / 4 + lat / 2), lcc->n);
1926 result.x = rho * sin(lon - lcc->reflon);
1927 result.y = lcc->rho0 - rho * cos(lcc->n * (lat - lcc->reflat));
1928
1929 return (result);
1930}
1931
1938bezier_t *
1939bezier_alloc(size_t n_pts)
1940{
1941 bezier_t *curve = safe_malloc(sizeof (*curve));
1942
1943 curve->n_pts = n_pts;
1944 curve->pts = safe_calloc(sizeof (vect2_t), n_pts);
1945
1946 return (curve);
1947}
1948
1952void
1954{
1955 free(curve->pts);
1956 free(curve);
1957}
1958
1974double
1975quad_bezier_func(double x, const bezier_t *func)
1976{
1977 ASSERT(func->n_pts >= 3 || func->n_pts % 3 == 2);
1978 /* Check boundary conditions */
1979 if (x < func->pts[0].x)
1980 return (func->pts[0].y);
1981 else if (x > func->pts[func->n_pts - 1].x)
1982 return (func->pts[func->n_pts - 1].y);
1983
1984 /* Point lies on a curve segment */
1985 for (size_t i = 0; i + 2 < func->n_pts; i += 2) {
1986 if (x <= func->pts[i + 2].x) {
1987 vect2_t p0 = func->pts[i], p1 = func->pts[i + 1];
1988 vect2_t p2 = func->pts[i + 2];
1989 double y, t, ts[2];
1990 unsigned n;
1991
1992 /*
1993 * Quadratic Bezier curves are defined as follows:
1994 *
1995 * B(t) = (1-t)^2.P0 + 2(1-t)t.P1 + t^2.P2
1996 *
1997 * Where 't' is a number 0.0 - 1.0, increasing along
1998 * curve as it progresses from P0 to P2 (P{0,1,2} are
1999 * 2D vectors). However, since we are using the curves
2000 * as functions, we first need to find which 't' along
2001 * the curve corresponds to our 'x' input.
2002 * Geometrically, quadratic Bezier curves are defined
2003 * as a set of points `P' which satisfy:
2004 *
2005 * 1) let `A' be a point that is on the line between
2006 * P0 and P1. Its distance from P0 is `t' times
2007 * the distance of P1 from P0.
2008 * 2) let `B' be a point that is on the line between
2009 * P1 and P2. Its distance from P1 is `t' times
2010 * the distance of P2 from P1.
2011 * 3) `P' is a point that is on a line between A and B.
2012 * Its distance from A is `t' times the distance
2013 * of B from A.
2014 *
2015 * The x coordinate of `P' corresponds to the input
2016 * `x' argument. Transforming this into an analytical
2017 * representation and simplifying to only consider
2018 * the X axis, we get (here 'a', 'b' represent the
2019 * respective 2D vector's X coordinate only):
2020 *
2021 * a = t(p1 - p0) + p0
2022 * b = t(p2 - p1) + p1
2023 * x = t(b - a) + a
2024 * x = t(t(p2 - p1) + p1 - t(p1 - p0) + p0) +
2025 * + t(p1 - p0) + p0
2026 *
2027 * Rearranging, we obtain this quadratic equation:
2028 *
2029 * 0 = (p2 - 2.p1 + p0)t^2 + 2(p1 - p0)t + p0 - x
2030 *
2031 * The solution we're interested in is only the one
2032 * between 0.0 - 1.0 (inclusive). We then take that
2033 * value and use it in the bezier curve equation at
2034 * the top to obtain the function value at point 'x'.
2035 */
2036 ASSERT3F(p0.x, <, p2.x);
2037 ASSERT3F(p0.x, <=, p1.x);
2038 ASSERT3F(p1.x, <=, p2.x);
2039 n = quadratic_solve(p2.x - 2 * p1.x + p0.x,
2040 2 * (p1.x - p0.x), p0.x - x, ts);
2041 ASSERT(n != 0);
2042 if (ts[0] >= 0 && ts[0] <= 1) {
2043 t = ts[0];
2044 } else if (n == 2 && ts[1] >= 0 && ts[1] <= 1) {
2045 t = ts[1];
2046 } else {
2047 continue;
2048 }
2049 ASSERT3F(t, >=, 0.0);
2050 ASSERT3F(t, <=, 1.0);
2051 y = POW2(1 - t) * p0.y + 2 * (1 - t) * t * p1.y +
2052 POW2(t) * p2.y;
2053
2054 return (y);
2055 }
2056 }
2057 VERIFY(0);
2058}
2059
2087double *
2088quad_bezier_func_inv(double y, const bezier_t *func, size_t *n_xs)
2089{
2090 double *xs = NULL;
2091 size_t num_xs = 0;
2092
2093 for (size_t i = 0; i + 2 < func->n_pts; i += 2) {
2094 vect2_t p0 = func->pts[i], p1 = func->pts[i + 1];
2095 vect2_t p2 = func->pts[i + 2];
2096 double t, ts[2];
2097 unsigned n;
2098
2099 if (p0.y == p1.y && p1.y == p2.y) {
2100 /* infinite number of results */
2101 free(xs);
2102 *n_xs = SIZE_MAX;
2103 return (NULL);
2104 }
2105
2106 /*
2107 * This quadratic equation is essentially the same as in
2108 * quad_bezier_func, except we used the `y' coordinates to
2109 * derive `t' and then calculate a corresponding `x' value.
2110 */
2111 n = quadratic_solve(p2.y - 2 * p1.y + p0.y, 2 * (p1.y - p0.y),
2112 p0.y - y, ts);
2113 for (unsigned i = 0; i < n; i++) {
2114 t = ts[i];
2115 if (t < 0.0 || t > 1.0)
2116 continue;
2117 num_xs++;
2118 xs = realloc(xs, num_xs * sizeof (*xs));
2119 xs[num_xs - 1] = POW2(1 - t) * p0.x +
2120 2 * (1 - t) * t * p1.x + POW2(t) * p2.x;
2121 }
2122 }
2123
2124 *n_xs = num_xs;
2125 return (xs);
2126}
2127
2131API_EXPORT void
2133{
2134 memset(mat, 0, sizeof (*mat));
2135 MAT4(mat, 0, 0) = 1;
2136 MAT4(mat, 1, 1) = 1;
2137 MAT4(mat, 2, 2) = 1;
2138 MAT4(mat, 3, 3) = 1;
2139}
2140
2144API_EXPORT void
2146{
2147 memset(mat, 0, sizeof (*mat));
2148 MAT3(mat, 0, 0) = 1;
2149 MAT3(mat, 1, 1) = 1;
2150 MAT3(mat, 2, 2) = 1;
2151}
#define VERIFY(x)
Definition assert.h:78
#define ASSERT3U(x, op, y)
Definition assert.h:210
#define ASSERT(x)
Definition assert.h:208
#define ASSERT3F(x, op, y)
Definition assert.h:211
#define VERIFY_MSG(x, fmt,...)
Definition assert.h:91
#define UNUSED_ATTR
Definition core.h:95
#define GEO2_TO_GEO3(v, a)
Definition geom.h:270
unsigned vect2sph_isect(vect3_t v, vect3_t o, vect3_t c, double r, bool_t confined, vect3_t i[2])
Definition geom.c:1035
vect3_t vect3_neg(vect3_t v)
Definition geom.c:569
vect2_t fpp_get_scale(const fpp_t *fpp)
Definition geom.c:1863
vect3l_t vect3l_xprod(vect3l_t a, vect3l_t b)
Definition geom.c:427
#define NULL_VECT3L
Definition geom.h:218
#define EARTH_MSL
Definition geom.h:285
geo_pos3_t ecmi2geo(vect3_t pos, double delta_t, const ellip_t *ellip)
Definition geom.c:712
vect3_t vect3_scmul(vect3_t a, double b)
Definition geom.c:355
vect2_t vect2vect_isect(vect2_t da, vect2_t oa, vect2_t db, vect2_t ob, bool_t confined)
Definition geom.c:1171
vect2_t vect2_add(vect2_t a, vect2_t b)
Definition geom.c:310
#define RAD2DEG(r)
Definition geom.h:158
fpp_t gnomo_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip, bool_t allow_inv)
Definition geom.c:1723
vect2_t vect2_rot(vect2_t v, double angle)
Definition geom.c:548
#define NULL_VECT3
Definition geom.h:216
vect3_t gl2ecef(vect3_t opengl)
Definition geom.c:781
vect3_t ecef2gl(vect3_t ecmi)
Definition geom.c:772
vect2_t geo2fpp(geo_pos2_t pos, const fpp_t *fpp)
Definition geom.c:1749
vect3_t vect3_xprod(vect3_t a, vect3_t b)
Definition geom.c:417
bool_t geo_pos3_from_str(const char *lat, const char *lon, const char *elev, geo_pos3_t *pos)
Definition geom.c:1454
vect2_t vect2_sub(vect2_t a, vect2_t b)
Definition geom.c:341
vect3_t vect3_mul(vect3_t a, vect3_t b)
Definition geom.c:163
#define ZERO_VECT3
Definition geom.h:210
vect3_t ecmi2ecef(vect3_t ecmi, double delta_t)
Definition geom.c:761
vect3_t vect3_add(vect3_t a, vect3_t b)
Definition geom.c:292
#define VECT3(x, y, z)
Definition geom.h:192
long double vect3l_dist(vect3l_t a, vect3l_t b)
Definition geom.c:142
double vect2_abs(vect2_t a)
Definition geom.c:151
long double vect3l_dotprod(vect3l_t a, vect3l_t b)
Definition geom.c:395
#define VECT2_EQ(a, b)
Definition geom.h:196
#define IS_NULL_VECT(a)
Definition geom.h:228
vect3_t vect3_local2acf(vect3_t v, double roll, double pitch, double hdgt)
Definition geom.c:600
#define ZERO_VECT3L
Definition geom.h:212
geo_pos2_t geo_displace_dir(const ellip_t *ellip, geo_pos2_t pos, vect2_t dir, double dist)
Definition geom.c:1417
vect3l_t ecef2gl_l(vect3l_t ecmi)
Definition geom.c:790
#define NULL_GEO_POS2
Definition geom.h:224
void fpp_set_scale(fpp_t *fpp, vect2_t scale)
Definition geom.c:1850
bool_t geo_pos2_from_str(const char *lat, const char *lon, geo_pos2_t *pos)
Definition geom.c:1442
sph_xlate_t sph_xlate_init(geo_pos2_t displacement, double rotation, bool_t inv)
Definition geom.c:1498
bool_t point_in_poly(vect2_t pt, const vect2_t *poly)
Definition geom.c:1340
geo_pos2_t fpp2geo(vect2_t pos, const fpp_t *fpp)
Definition geom.c:1787
vect3_t sph2ecef(geo_pos3_t pos)
Definition geom.c:663
fpp_t fpp_init(geo_pos2_t center, double rot, double dist, const ellip_t *ellip, bool_t allow_inv)
Definition geom.c:1680
double gc_point_hdg(geo_pos2_t start, geo_pos2_t end)
Definition geom.c:1637
vect3_t geo2ecef_mtr(geo_pos3_t pos, const ellip_t *ellip)
Definition geom.c:861
vect3_t ecef2ecmi(vect3_t ecef, double delta_t)
Definition geom.c:749
geo_pos2_t sph_xlate(geo_pos2_t pos, const sph_xlate_t *xlate)
Definition geom.c:1602
vect3l_t gl2ecef_l(vect3l_t opengl)
Definition geom.c:799
unsigned vect2poly_isect(vect2_t a, vect2_t oa, const vect2_t *poly)
Definition geom.c:1326
vect3l_t vect3l_mul(vect3l_t a, vect3l_t b)
Definition geom.c:172
ellip_t ellip_init(double semi_major, double semi_minor, double flattening)
Definition geom.c:811
vect2_t vect2_scmul(vect2_t a, double b)
Definition geom.c:373
vect3_t vect3_acf2local(vect3_t v, double roll, double pitch, double hdgt)
Definition geom.c:610
fpp_t stereo_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip, bool_t allow_inv)
Definition geom.c:1736
double quad_bezier_func(double x, const bezier_t *func)
Definition geom.c:1975
bezier_t * bezier_alloc(size_t num_pts)
Definition geom.c:1939
geo_pos3_t ecmi2sph(vect3_t pos, double delta_t)
Definition geom.c:737
double fpp_get_gc_distance(vect2_t p1, vect2_t p2, const fpp_t *fpp)
Definition geom.c:1875
#define GEO_POS3(lat, lon, elev)
Definition geom.h:166
vect3_t vect3_set_abs(vect3_t a, double abs)
Definition geom.c:201
vect3_t vect3_rot(vect3_t v, double angle, unsigned axis)
Definition geom.c:479
void bezier_free(bezier_t *curve)
Definition geom.c:1953
vect2_t vect2_neg(vect2_t v)
Definition geom.c:587
double vect3_dotprod(vect3_t a, vect3_t b)
Definition geom.c:386
vect3l_t vect3l_scmul(vect3l_t a, long double b)
Definition geom.c:364
double gc_distance(geo_pos2_t start, geo_pos2_t end)
Definition geom.c:1615
#define IS_NULL_GEO_POS(a)
Definition geom.h:239
#define VECT3L(x, y, z)
Definition geom.h:194
void mat3_ident(mat3_t *mat)
Definition geom.c:2145
double dir2hdg(vect2_t dir)
Definition geom.c:1371
#define GEO_POS2(lat, lon)
Definition geom.h:164
#define GEO3_TO_GEO2(v)
Definition geom.h:272
vect2_t vect2_mul(vect2_t a, vect2_t b)
Definition geom.c:181
vect3_t vect3_sub(vect3_t a, vect3_t b)
Definition geom.c:323
vect3l_t vect3l_sub(vect3l_t a, vect3l_t b)
Definition geom.c:332
vect3_t geo2ecmi(geo_pos3_t pos, double delta_t, const ellip_t *ellip)
Definition geom.c:695
bool_t is_on_arc(double angle_x, double angle1, double angle2, bool_t cw)
Definition geom.c:93
vect2_t vect2_set_abs(vect2_t a, double abs)
Definition geom.c:227
double vect3_dist(vect3_t a, vect3_t b)
Definition geom.c:133
vect2_t vect2_mean(vect2_t a, vect2_t b)
Definition geom.c:466
vect3l_t vect3l_set_abs(vect3l_t a, long double abs)
Definition geom.c:214
double * quad_bezier_func_inv(double y, const bezier_t *func, size_t *n_xs)
Definition geom.c:2088
geo_pos3_t geo2sph(geo_pos3_t pos, const ellip_t *ellip)
Definition geom.c:831
unsigned vect2poly_isect_get(vect2_t a, vect2_t oa, const vect2_t *poly, vect2_t *isects, unsigned cap)
Definition geom.c:1291
double vect2_dist(vect2_t a, vect2_t b)
Definition geom.c:190
double rel_angle(double a1, double a2)
Definition geom.c:623
vect3_t sph2ecmi(geo_pos3_t pos, double delta_t)
Definition geom.c:725
vect3_t vect3_mean(vect3_t a, vect3_t b)
Definition geom.c:448
geo_pos3_t ecef2geo(vect3_t pos, const ellip_t *ellip)
Definition geom.c:900
vect2_t vect2_norm(vect2_t v, bool_t right)
Definition geom.c:536
vect3_t sph_xlate_vect(vect3_t pos, const sph_xlate_t *xlate)
Definition geom.c:1565
double vect3_abs(vect3_t a)
Definition geom.c:115
unsigned circ2circ_isect(vect2_t ca, double ra, vect2_t cb, double rb, vect2_t i[2])
Definition geom.c:1220
fpp_t ortho_fpp_init(geo_pos2_t center, double rot, const ellip_t *ellip, bool_t allow_inv)
Definition geom.c:1711
unsigned vect2circ_isect(vect2_t v, vect2_t o, vect2_t c, double r, bool_t confined, vect2_t i[2])
Definition geom.c:1131
const ellip_t wgs84
Definition geom.c:38
#define DEG2RAD(d)
Definition geom.h:156
vect3l_t vect3l_add(vect3l_t a, vect3l_t b)
Definition geom.c:301
#define NULL_VECT2
Definition geom.h:214
double vect2_dotprod(vect2_t a, vect2_t b)
Definition geom.c:404
vect2_t vect2_rot_inv_y(vect2_t v, double angle)
Definition geom.c:559
geo_pos2_t geo_displace(const ellip_t *ellip, geo_pos2_t pos, double truehdg, double dist)
Definition geom.c:1405
long double vect3l_abs(vect3l_t a)
Definition geom.c:124
lcc_t lcc_init(double reflat, double reflon, double stdpar1, double stdpar2)
Definition geom.c:1892
vect3_t geo2ecef_ft(geo_pos3_t pos, const ellip_t *ellip)
Definition geom.c:884
vect3l_t vect3l_neg(vect3l_t v)
Definition geom.c:578
vect2_t geo2lcc(geo_pos2_t pos, const lcc_t *lcc)
Definition geom.c:1918
#define ZERO_VECT2
Definition geom.h:208
vect3_t vect3_unit(vect3_t a, double *l)
Definition geom.c:242
#define VECT2(x, y)
Definition geom.h:190
vect2_t vect2_unit(vect2_t a, double *l)
Definition geom.c:272
vect3l_t vect3l_mean(vect3l_t a, vect3l_t b)
Definition geom.c:457
vect3l_t vect3l_rot(vect3l_t v, long double angle, unsigned axis)
Definition geom.c:507
geo_pos3_t ecef2sph(vect3_t v)
Definition geom.c:987
vect2_t hdg2dir(double truehdg)
Definition geom.c:1359
void mat4_ident(mat4_t *mat)
Definition geom.c:2132
static bool_t is_valid_elev(double elev)
Definition helpers.h:123
static double normalize_hdg(double hdg)
Definition helpers.h:215
static bool_t is_valid_lat(double lat)
Definition helpers.h:93
static bool_t is_valid_lon(double lon)
Definition helpers.h:111
#define POW2(x)
Definition math.h:36
unsigned quadratic_solve(double a, double b, double c, double x[2])
Definition math.c:33
#define POW3(x)
Definition math.h:34
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71
static void * safe_malloc(size_t size)
Definition safe_alloc.h:56
double a
Definition geom.h:131
double b
Definition geom.h:132
double ecc2
Definition geom.h:135
Definition geom.h:473
double lat
Definition geom.h:50
double lon
Definition geom.h:51
double lat
Definition geom.h:41
double elev
Definition geom.h:43
double lon
Definition geom.h:42
Definition geom.h:500
Definition geom.h:528
Definition geom.h:524
Definition geom.h:89
#define REQ_PTR(x)
Definition sysmacros.h:334