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
math.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 2017 Saso Kiselkov. All rights reserved.
17 */
18
19#include <math.h>
20
21#include "acfutils/geom.h"
22#include "acfutils/helpers.h"
23#include "acfutils/math.h"
24#include "acfutils/safe_alloc.h"
25
26#define ROUND_ERROR 1e-10
27
32unsigned
33quadratic_solve(double a, double b, double c, double x[2])
34{
35 double tmp;
36
37 /* Actually just a linear equation. */
38 if (a == 0) {
39 if (b == 0)
40 return (0);
41 x[0] = -c / b;
42 return (1);
43 }
44
45 tmp = POW2(b) - 4 * a * c;
46 if (tmp > ROUND_ERROR) {
47 double tmp_sqrt = sqrt(tmp);
48 x[0] = (-b + tmp_sqrt) / (2 * a);
49 x[1] = (-b - tmp_sqrt) / (2 * a);
50 return (2);
51 } else if (tmp > -ROUND_ERROR) {
52 x[0] = -b / (2 * a);
53 return (1);
54 } else {
55 return (0);
56 }
57}
58
68double
69fx_lin(double x, double x1, double y1, double x2, double y2)
70{
71 ASSERT3F(x1, !=, x2);
72 return (((x - x1) / (x2 - x1)) * (y2 - y1) + y1);
73}
74
75static inline size_t
76count_points_sentinel(const vect2_t *points)
77{
78 size_t n_points;
79 ASSERT(points != NULL);
80 ASSERT(!IS_NULL_VECT(points[0]));
81 ASSERT(!IS_NULL_VECT(points[1]));
82 for (n_points = 2; !IS_NULL_VECT(points[2]); n_points++)
83 points++;
84 return (n_points);
85}
86
96double
97fx_lin_multi(double x, const struct vect2_s *points, bool_t extrapolate)
98{
99 return (fx_lin_multi2(x, points, count_points_sentinel(points),
100 extrapolate));
101}
102
112double
113fx_lin_multi2(double x, const struct vect2_s *points, size_t n_points,
114 bool_t extrapolate)
115{
116 ASSERT(points != NULL);
117 ASSERT3U(n_points, >=, 2);
118
119 for (;;) {
120 vect2_t p1 = points[0], p2 = points[1];
121
122 ASSERT3F(p1.x, <, p2.x);
123
124 if (x < p1.x) {
125 /* X outside of range to the left */
126 if (extrapolate)
127 return (fx_lin(x, p1.x, p1.y, p2.x, p2.y));
128 break;
129 }
130 /* X in range of current segment */
131 if (x <= p2.x)
132 return (fx_lin(x, p1.x, p1.y, p2.x, p2.y));
133 /* X outside of range to the right */
134 if (n_points == 2) {
135 if (extrapolate)
136 return (fx_lin(x, p1.x, p1.y, p2.x, p2.y));
137 break;
138 }
139
140 points++;
141 n_points--;
142 }
143
144 return (NAN);
145}
146
166double *
167fx_lin_multi_inv(double y, const struct vect2_s *points, size_t *num_out)
168{
169 return (fx_lin_multi_inv3(y, points, count_points_sentinel(points),
170 B_FALSE, num_out));
171}
172
184double *
185fx_lin_multi_inv2(double y, const struct vect2_s *points, bool_t extrapolate,
186 size_t *num_out)
187{
188 return (fx_lin_multi_inv3(y, points, count_points_sentinel(points),
189 extrapolate, num_out));
190}
191
197double *
198fx_lin_multi_inv3(double y, const struct vect2_s *points, size_t n_points,
199 bool_t extrapolate, size_t *num_out)
200{
201 double *out = NULL;
202 size_t cap = 0, num = 0;
203
204 ASSERT(points != NULL);
205 ASSERT(num_out != NULL);
206 ASSERT3U(n_points, >=, 2);
207
208 for (size_t i = 0; i + 1 < n_points; i++) {
209 vect2_t p1 = points[i], p2 = points[i + 1];
210 double min_val = MIN(p1.y, p2.y);
211 double max_val = MAX(p1.y, p2.y);
212
213 if (min_val <= y && y <= max_val)
214 cap++;
215 }
216 if (extrapolate)
217 cap += 2;
218 if (cap == 0) {
219 *num_out = 0;
220 return (NULL);
221 }
222
223 out = safe_calloc(cap, sizeof (*out));
224 for (size_t i = 0; i + 1 < n_points; i++) {
225 vect2_t p1 = points[i], p2 = points[i + 1];
226 double min_val = MIN(p1.y, p2.y);
227 double max_val = MAX(p1.y, p2.y);
228
229 if (extrapolate) {
230 bool_t first = (i == 0);
231 bool_t up_slope = (p1.y <= p2.y);
232 if (first && ((up_slope && y < p1.y) ||
233 (!up_slope && y > p1.y)) && p1.y != p2.y) {
234 out[num++] = fx_lin(y, p1.y, p1.x, p2.y, p2.x);
235 }
236 }
237 if (min_val <= y && y <= max_val)
238 out[num++] = fx_lin(y, p1.y, p1.x, p2.y, p2.x);
239 if (extrapolate) {
240 bool_t last = IS_NULL_VECT(points[i + 2]);
241 bool_t up_slope = (p1.y <= p2.y);
242 if (last && ((up_slope && y > p2.y) ||
243 (!up_slope && y < p2.y)) && p1.y != p2.y) {
244 out[num++] = fx_lin(y, p1.y, p1.x, p2.y, p2.x);
245 }
246 }
247 }
248 /* We might have slightly over-accounted here if extrapolate was set */
249 *num_out = num;
250
251 return (out);
252}
253
267void
268pn_interp_init(pn_interp_t *interp, const vect2_t *points, unsigned numpts)
269{
270 ASSERT(interp != NULL);
271 ASSERT(points != 0);
272 ASSERT(numpts != 0);
273 ASSERT3U(numpts, <=, MAX_PN_INTERP_ORDER);
274
275 memset(interp, 0, sizeof (*interp));
276 interp->order = numpts;
277
278 for (unsigned i = 0; i < numpts; i++) {
279 double terms[MAX_PN_INTERP_ORDER] = { 0 };
280 double product = 1.0;
281
282 /* Compute Prod_{j != i} (x_i - x_j) */
283 for (unsigned j = 0; j < numpts; j++) {
284 if (i == j)
285 continue;
286 product *= points[i].x - points[j].x;
287 }
288 /* Compute y_i/Prod_{j != i} (x_i - x_j) */
289 product = points[i].y / product;
290 terms[0] = product;
291 /* Compute theterm := product * Prod_{j != i} (x - x_j) */
292 for (unsigned j = 0; j < numpts; j++) {
293 if (i == j)
294 continue;
295 for (int k = numpts - 1; k > 0; k--) {
296 terms[k] += terms[k-1];
297 terms[k-1] *= -points[j].x;
298 }
299 }
300 /* coeff += terms (as coeff vectors) */
301 for (unsigned j = 0; j < numpts; j++)
302 interp->coeff[j] += terms[j];
303 }
304}
#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 IS_NULL_VECT(a)
Definition geom.h:228
double * fx_lin_multi_inv(double y, const struct vect2_s *points, size_t *num_out)
Definition math.c:167
#define POW2(x)
Definition math.h:36
double * fx_lin_multi_inv3(double y, const struct vect2_s *points, size_t n_points, bool_t extrapolate, size_t *num_out)
Definition math.c:198
double fx_lin(double x, double x1, double y1, double x2, double y2)
Definition math.c:69
void pn_interp_init(pn_interp_t *interp, const vect2_t *points, unsigned npts)
Definition math.c:268
unsigned quadratic_solve(double a, double b, double c, double x[2])
Definition math.c:33
double fx_lin_multi2(double x, const struct vect2_s *points, size_t n_points, bool_t extrapolate)
Definition math.c:113
double fx_lin_multi(double x, const struct vect2_s *points, bool_t extrapolate)
Definition math.c:97
double * fx_lin_multi_inv2(double y, const struct vect2_s *points, bool_t extrapolate, size_t *num_out)
Definition math.c:185
static void * safe_calloc(size_t nmemb, size_t size)
Definition safe_alloc.h:71