/src/postgis/liblwgeom/lwgeom_api.c
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * PostGIS - Spatial Types for PostgreSQL |
4 | | * http://postgis.net |
5 | | * |
6 | | * PostGIS is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation, either version 2 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * PostGIS is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with PostGIS. If not, see <http://www.gnu.org/licenses/>. |
18 | | * |
19 | | ********************************************************************** |
20 | | * |
21 | | * Copyright 2001-2006 Refractions Research Inc. |
22 | | * Copyright 2017-2026 Darafei Praliaskouski <me@komzpa.net> |
23 | | * |
24 | | **********************************************************************/ |
25 | | |
26 | | #include "liblwgeom_internal.h" |
27 | | #include "lwgeom_log.h" |
28 | | |
29 | | #include <stdio.h> |
30 | | #include <assert.h> |
31 | | |
32 | 0 | #define xstr(s) str(s) |
33 | 0 | #define str(s) #s |
34 | | |
35 | | const char * |
36 | | lwgeom_version(void) |
37 | 0 | { |
38 | 0 | static char *ptr = NULL; |
39 | 0 | static char buf[256]; |
40 | 0 | if ( ! ptr ) |
41 | 0 | { |
42 | 0 | ptr = buf; |
43 | 0 | snprintf(ptr, 256, LIBLWGEOM_VERSION" " xstr(POSTGIS_REVISION)); |
44 | 0 | } |
45 | |
|
46 | 0 | return ptr; |
47 | 0 | } |
48 | | |
49 | | |
50 | | inline float |
51 | | next_float_down(double d) |
52 | 10.9M | { |
53 | 10.9M | float result; |
54 | 10.9M | if (d > (double)FLT_MAX) |
55 | 21.6k | return FLT_MAX; |
56 | 10.9M | if (d <= (double)-FLT_MAX) |
57 | 57.1k | return -FLT_MAX; |
58 | 10.8M | result = d; |
59 | | |
60 | 10.8M | if ( ((double)result) <=d ) |
61 | 10.8M | return result; |
62 | | |
63 | 62.0k | return nextafterf(result, -1*FLT_MAX); |
64 | | |
65 | 10.8M | } |
66 | | |
67 | | /* |
68 | | * Returns the float that's very close to the input, but >=. |
69 | | * handles the funny differences in float4 and float8 reps. |
70 | | */ |
71 | | inline float |
72 | | next_float_up(double d) |
73 | 10.9M | { |
74 | 10.9M | float result; |
75 | 10.9M | if (d >= (double)FLT_MAX) |
76 | 28.0k | return FLT_MAX; |
77 | 10.9M | if (d < (double)-FLT_MAX) |
78 | 37.1k | return -FLT_MAX; |
79 | 10.8M | result = d; |
80 | | |
81 | 10.8M | if ( ((double)result) >=d ) |
82 | 10.7M | return result; |
83 | | |
84 | 114k | return nextafterf(result, FLT_MAX); |
85 | 10.8M | } |
86 | | |
87 | | |
88 | | |
89 | | |
90 | | /************************************************************************ |
91 | | * POINTARRAY support functions |
92 | | * |
93 | | * TODO: should be moved to ptarray.c probably |
94 | | * |
95 | | ************************************************************************/ |
96 | | |
97 | | /* |
98 | | * Copies a point from the point array into the parameter point |
99 | | * will set point's z=NO_Z_VALUE if pa is 2d |
100 | | * will set point's m=NO_M_VALUE if pa is 3d or 2d |
101 | | * |
102 | | * NOTE: point is a real POINT3D *not* a pointer |
103 | | */ |
104 | | POINT4D |
105 | | getPoint4d(const POINTARRAY *pa, uint32_t n) |
106 | 0 | { |
107 | 0 | POINT4D result; |
108 | 0 | if (getPoint4d_p(pa, n, &result) == 0) |
109 | 0 | lwerror("%s [%d] error returned by getPoint4d_p", __FILE__, __LINE__); |
110 | 0 | return result; |
111 | 0 | } |
112 | | |
113 | | /* |
114 | | * Copies a point from the point array into the parameter point |
115 | | * will set point's z=NO_Z_VALUE if pa is 2d |
116 | | * will set point's m=NO_M_VALUE if pa is 3d or 2d |
117 | | * |
118 | | * NOTE: this will modify the point4d pointed to by 'point'. |
119 | | * |
120 | | * @return 0 on error, 1 on success |
121 | | */ |
122 | | int |
123 | | getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *op) |
124 | 15.2M | { |
125 | 15.2M | uint8_t *ptr; |
126 | 15.2M | int zmflag; |
127 | | |
128 | 15.2M | if ( ! pa ) |
129 | 0 | { |
130 | 0 | lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); |
131 | 0 | return 0; |
132 | 0 | } |
133 | | |
134 | 15.2M | if ( n>=pa->npoints ) |
135 | 40 | { |
136 | 40 | lwerror("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); |
137 | 40 | return 0; |
138 | 40 | } |
139 | | |
140 | 15.2M | LWDEBUG(4, "getPoint4d_p called."); |
141 | | |
142 | | /* Get a pointer to nth point offset and zmflag */ |
143 | 15.2M | ptr=getPoint_internal(pa, n); |
144 | 15.2M | zmflag=FLAGS_GET_ZM(pa->flags); |
145 | | |
146 | 15.2M | LWDEBUGF(4, "ptr %p, zmflag %d", ptr, zmflag); |
147 | | |
148 | 15.2M | switch (zmflag) |
149 | 15.2M | { |
150 | 7.34M | case 0: /* 2d */ |
151 | 7.34M | memcpy(op, ptr, sizeof(POINT2D)); |
152 | 7.34M | op->m=NO_M_VALUE; |
153 | 7.34M | op->z=NO_Z_VALUE; |
154 | 7.34M | break; |
155 | | |
156 | 278k | case 3: /* ZM */ |
157 | 278k | memcpy(op, ptr, sizeof(POINT4D)); |
158 | 278k | break; |
159 | | |
160 | 4.62M | case 2: /* Z */ |
161 | 4.62M | memcpy(op, ptr, sizeof(POINT3DZ)); |
162 | 4.62M | op->m=NO_M_VALUE; |
163 | 4.62M | break; |
164 | | |
165 | 2.98M | case 1: /* M */ |
166 | 2.98M | memcpy(op, ptr, sizeof(POINT3DM)); |
167 | 2.98M | op->m=op->z; /* we use Z as temporary storage */ |
168 | 2.98M | op->z=NO_Z_VALUE; |
169 | 2.98M | break; |
170 | | |
171 | 0 | default: |
172 | 0 | lwerror("Unknown ZM flag ??"); |
173 | 0 | return 0; |
174 | 15.2M | } |
175 | 15.2M | return 1; |
176 | | |
177 | 15.2M | } |
178 | | |
179 | | /* |
180 | | * Copy a point from the point array into the parameter point |
181 | | * will set point's z=NO_Z_VALUE if pa is 2d |
182 | | * NOTE: point is a real POINT3DZ *not* a pointer |
183 | | */ |
184 | | POINT3DZ |
185 | | getPoint3dz(const POINTARRAY *pa, uint32_t n) |
186 | 0 | { |
187 | 0 | POINT3DZ result; |
188 | 0 | getPoint3dz_p(pa, n, &result); |
189 | 0 | return result; |
190 | 0 | } |
191 | | |
192 | | /* |
193 | | * Copy a point from the point array into the parameter point |
194 | | * will set point's z=NO_Z_VALUE if pa is 2d |
195 | | * |
196 | | * NOTE: point is a real POINT3DZ *not* a pointer |
197 | | */ |
198 | | POINT3DM |
199 | | getPoint3dm(const POINTARRAY *pa, uint32_t n) |
200 | 0 | { |
201 | 0 | POINT3DM result; |
202 | 0 | getPoint3dm_p(pa, n, &result); |
203 | 0 | return result; |
204 | 0 | } |
205 | | |
206 | | /* |
207 | | * Copy a point from the point array into the parameter point |
208 | | * will set point's z=NO_Z_VALUE if pa is 2d |
209 | | * |
210 | | * NOTE: this will modify the point3dz pointed to by 'point'. |
211 | | */ |
212 | | int |
213 | | getPoint3dz_p(const POINTARRAY *pa, uint32_t n, POINT3DZ *op) |
214 | 0 | { |
215 | 0 | uint8_t *ptr; |
216 | |
|
217 | 0 | if ( ! pa ) |
218 | 0 | { |
219 | 0 | lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); |
220 | 0 | return 0; |
221 | 0 | } |
222 | | |
223 | | //assert(n < pa->npoints); --causes point empty/point empty to crash |
224 | 0 | if ( n>=pa->npoints ) |
225 | 0 | { |
226 | 0 | lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); |
227 | 0 | return 0; |
228 | 0 | } |
229 | | |
230 | 0 | LWDEBUGF(2, "getPoint3dz_p called on array of %d-dimensions / %u pts", |
231 | 0 | FLAGS_NDIMS(pa->flags), pa->npoints); |
232 | | |
233 | | /* Get a pointer to nth point offset */ |
234 | 0 | ptr=getPoint_internal(pa, n); |
235 | | |
236 | | /* |
237 | | * if input POINTARRAY has the Z, it is always |
238 | | * at third position so make a single copy |
239 | | */ |
240 | 0 | if ( FLAGS_GET_Z(pa->flags) ) |
241 | 0 | { |
242 | 0 | memcpy(op, ptr, sizeof(POINT3DZ)); |
243 | 0 | } |
244 | | |
245 | | /* |
246 | | * Otherwise copy the 2d part and initialize |
247 | | * Z to NO_Z_VALUE |
248 | | */ |
249 | 0 | else |
250 | 0 | { |
251 | 0 | memcpy(op, ptr, sizeof(POINT2D)); |
252 | 0 | op->z=NO_Z_VALUE; |
253 | 0 | } |
254 | |
|
255 | 0 | return 1; |
256 | |
|
257 | 0 | } |
258 | | |
259 | | /* |
260 | | * Copy a point from the point array into the parameter point |
261 | | * will set point's m=NO_Z_VALUE if pa has no M |
262 | | * |
263 | | * NOTE: this will modify the point3dm pointed to by 'point'. |
264 | | */ |
265 | | int |
266 | | getPoint3dm_p(const POINTARRAY *pa, uint32_t n, POINT3DM *op) |
267 | 0 | { |
268 | 0 | uint8_t *ptr; |
269 | 0 | int zmflag; |
270 | |
|
271 | 0 | if (!pa) |
272 | 0 | { |
273 | 0 | lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); |
274 | 0 | return LW_FALSE; |
275 | 0 | } |
276 | | |
277 | 0 | if (n >= pa->npoints) |
278 | 0 | { |
279 | 0 | lwerror("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); |
280 | 0 | return LW_FALSE; |
281 | 0 | } |
282 | | |
283 | | /* Get a pointer to nth point offset and zmflag */ |
284 | 0 | ptr = getPoint_internal(pa, n); |
285 | 0 | zmflag = FLAGS_GET_ZM(pa->flags); |
286 | | |
287 | | /* |
288 | | * if input POINTARRAY has the M and NO Z, |
289 | | * we can issue a single memcpy |
290 | | */ |
291 | 0 | if (zmflag == 1) |
292 | 0 | { |
293 | 0 | memcpy(op, ptr, sizeof(POINT3DM)); |
294 | 0 | return LW_TRUE; |
295 | 0 | } |
296 | | |
297 | | /* |
298 | | * Otherwise copy the 2d part and |
299 | | * initialize M to NO_M_VALUE |
300 | | */ |
301 | 0 | memcpy(op, ptr, sizeof(POINT2D)); |
302 | | |
303 | | /* |
304 | | * Then, if input has Z skip it and |
305 | | * copy next double, otherwise initialize |
306 | | * M to NO_M_VALUE |
307 | | */ |
308 | 0 | if (zmflag == 3) |
309 | 0 | { |
310 | 0 | ptr += sizeof(POINT3DZ); |
311 | 0 | memcpy(&(op->m), ptr, sizeof(double)); |
312 | 0 | } |
313 | 0 | else |
314 | 0 | op->m = NO_M_VALUE; |
315 | |
|
316 | 0 | return LW_TRUE; |
317 | 0 | } |
318 | | |
319 | | /* |
320 | | * Copy a point from the point array into the parameter point |
321 | | * z value (if present) is not returned. |
322 | | * |
323 | | * NOTE: point is a real POINT2D *not* a pointer |
324 | | */ |
325 | | POINT2D |
326 | | getPoint2d(const POINTARRAY *pa, uint32_t n) |
327 | 0 | { |
328 | 0 | const POINT2D *result; |
329 | 0 | result = getPoint2d_cp(pa, n); |
330 | 0 | return *result; |
331 | 0 | } |
332 | | |
333 | | /* |
334 | | * Copy a point from the point array into the parameter point |
335 | | * z value (if present) is not returned. |
336 | | * |
337 | | * NOTE: this will modify the point2d pointed to by 'point'. |
338 | | */ |
339 | | int |
340 | | getPoint2d_p(const POINTARRAY *pa, uint32_t n, POINT2D *point) |
341 | 0 | { |
342 | 0 | if ( ! pa ) |
343 | 0 | { |
344 | 0 | lwerror("%s [%d] NULL POINTARRAY input", __FILE__, __LINE__); |
345 | 0 | return 0; |
346 | 0 | } |
347 | | |
348 | 0 | if ( n>=pa->npoints ) |
349 | 0 | { |
350 | 0 | lwnotice("%s [%d] called with n=%d and npoints=%d", __FILE__, __LINE__, n, pa->npoints); |
351 | 0 | return 0; |
352 | 0 | } |
353 | | |
354 | | /* this does x,y */ |
355 | 0 | memcpy(point, getPoint_internal(pa, n), sizeof(POINT2D)); |
356 | 0 | return 1; |
357 | 0 | } |
358 | | |
359 | | /* |
360 | | * set point N to the given value |
361 | | * NOTE that the pointarray can be of any |
362 | | * dimension, the appropriate ordinate values |
363 | | * will be extracted from it |
364 | | * |
365 | | */ |
366 | | void |
367 | | ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d) |
368 | 2.30M | { |
369 | 2.30M | uint8_t *ptr; |
370 | 2.30M | assert(n < pa->npoints); |
371 | 2.30M | ptr = getPoint_internal(pa, n); |
372 | 2.30M | switch ( FLAGS_GET_ZM(pa->flags) ) |
373 | 2.30M | { |
374 | 45.9k | case 3: |
375 | 45.9k | memcpy(ptr, p4d, sizeof(POINT4D)); |
376 | 45.9k | break; |
377 | 557k | case 2: |
378 | 557k | memcpy(ptr, p4d, sizeof(POINT3DZ)); |
379 | 557k | break; |
380 | 107k | case 1: |
381 | 107k | memcpy(ptr, p4d, sizeof(POINT2D)); |
382 | 107k | ptr+=sizeof(POINT2D); |
383 | 107k | memcpy(ptr, &(p4d->m), sizeof(double)); |
384 | 107k | break; |
385 | 1.59M | case 0: |
386 | 1.59M | memcpy(ptr, p4d, sizeof(POINT2D)); |
387 | 1.59M | break; |
388 | 2.30M | } |
389 | 2.30M | } |
390 | | |
391 | | void |
392 | | ptarray_copy_point(POINTARRAY *pa, uint32_t from, uint32_t to) |
393 | 0 | { |
394 | 0 | int ndims = FLAGS_NDIMS(pa->flags); |
395 | 0 | switch (ndims) |
396 | 0 | { |
397 | 0 | case 2: |
398 | 0 | { |
399 | 0 | POINT2D *p_from = (POINT2D*)(getPoint_internal(pa, from)); |
400 | 0 | POINT2D *p_to = (POINT2D*)(getPoint_internal(pa, to)); |
401 | 0 | *p_to = *p_from; |
402 | 0 | return; |
403 | 0 | } |
404 | 0 | case 3: |
405 | 0 | { |
406 | 0 | POINT3D *p_from = (POINT3D*)(getPoint_internal(pa, from)); |
407 | 0 | POINT3D *p_to = (POINT3D*)(getPoint_internal(pa, to)); |
408 | 0 | *p_to = *p_from; |
409 | 0 | return; |
410 | 0 | } |
411 | 0 | case 4: |
412 | 0 | { |
413 | 0 | POINT4D *p_from = (POINT4D*)(getPoint_internal(pa, from)); |
414 | 0 | POINT4D *p_to = (POINT4D*)(getPoint_internal(pa, to)); |
415 | 0 | *p_to = *p_from; |
416 | 0 | return; |
417 | 0 | } |
418 | 0 | default: |
419 | 0 | { |
420 | 0 | lwerror("%s: unsupported number of dimensions - %d", __func__, ndims); |
421 | 0 | return; |
422 | 0 | } |
423 | 0 | } |
424 | 0 | return; |
425 | 0 | } |
426 | | |
427 | | |
428 | | /************************************************ |
429 | | * debugging routines |
430 | | ************************************************/ |
431 | | |
432 | | void printBOX3D(BOX3D *box) |
433 | 0 | { |
434 | 0 | lwnotice("BOX3D: %g %g, %g %g", box->xmin, box->ymin, |
435 | 0 | box->xmax, box->ymax); |
436 | 0 | } |
437 | | |
438 | | void printPA(POINTARRAY *pa) |
439 | 0 | { |
440 | 0 | uint32_t t; |
441 | 0 | POINT4D pt; |
442 | 0 | char *mflag; |
443 | |
|
444 | 0 | if (!pa) |
445 | 0 | { |
446 | 0 | lwnotice(" PTARRAY is null pointer!"); |
447 | 0 | return; |
448 | 0 | } |
449 | 0 | if ( FLAGS_GET_M(pa->flags) ) mflag = "M"; |
450 | 0 | else mflag = ""; |
451 | |
|
452 | 0 | lwnotice(" POINTARRAY%s{", mflag); |
453 | 0 | lwnotice( |
454 | 0 | " ndims=%i, ptsize=%lu", FLAGS_NDIMS(pa->flags), (unsigned long)ptarray_point_size(pa)); |
455 | 0 | lwnotice(" npoints = %u", pa->npoints); |
456 | |
|
457 | 0 | for (t = 0; t < pa->npoints; t++) |
458 | 0 | { |
459 | 0 | getPoint4d_p(pa, t, &pt); |
460 | 0 | if (FLAGS_NDIMS(pa->flags) == 2) |
461 | 0 | lwnotice(" %i : %lf,%lf", t, pt.x, pt.y); |
462 | 0 | if (FLAGS_NDIMS(pa->flags) == 3) |
463 | 0 | lwnotice(" %i : %lf,%lf,%lf", t, pt.x, pt.y, pt.z); |
464 | 0 | if (FLAGS_NDIMS(pa->flags) == 4) |
465 | 0 | lwnotice(" %i : %lf,%lf,%lf,%lf", t, pt.x, pt.y, pt.z, pt.m); |
466 | 0 | } |
467 | |
|
468 | 0 | lwnotice(" }"); |
469 | 0 | } |
470 | | |
471 | | |
472 | | /** |
473 | | * Given a string with at least 2 chars in it, convert them to |
474 | | * a byte value. No error checking done! |
475 | | */ |
476 | | uint8_t |
477 | | parse_hex(char *str) |
478 | 0 | { |
479 | | /* do this a little brute force to make it faster */ |
480 | |
|
481 | 0 | uint8_t result_high = 0; |
482 | 0 | uint8_t result_low = 0; |
483 | |
|
484 | 0 | switch (str[0]) |
485 | 0 | { |
486 | 0 | case '0' : |
487 | 0 | result_high = 0; |
488 | 0 | break; |
489 | 0 | case '1' : |
490 | 0 | result_high = 1; |
491 | 0 | break; |
492 | 0 | case '2' : |
493 | 0 | result_high = 2; |
494 | 0 | break; |
495 | 0 | case '3' : |
496 | 0 | result_high = 3; |
497 | 0 | break; |
498 | 0 | case '4' : |
499 | 0 | result_high = 4; |
500 | 0 | break; |
501 | 0 | case '5' : |
502 | 0 | result_high = 5; |
503 | 0 | break; |
504 | 0 | case '6' : |
505 | 0 | result_high = 6; |
506 | 0 | break; |
507 | 0 | case '7' : |
508 | 0 | result_high = 7; |
509 | 0 | break; |
510 | 0 | case '8' : |
511 | 0 | result_high = 8; |
512 | 0 | break; |
513 | 0 | case '9' : |
514 | 0 | result_high = 9; |
515 | 0 | break; |
516 | 0 | case 'A' : |
517 | 0 | case 'a' : |
518 | 0 | result_high = 10; |
519 | 0 | break; |
520 | 0 | case 'B' : |
521 | 0 | case 'b' : |
522 | 0 | result_high = 11; |
523 | 0 | break; |
524 | 0 | case 'C' : |
525 | 0 | case 'c' : |
526 | 0 | result_high = 12; |
527 | 0 | break; |
528 | 0 | case 'D' : |
529 | 0 | case 'd' : |
530 | 0 | result_high = 13; |
531 | 0 | break; |
532 | 0 | case 'E' : |
533 | 0 | case 'e' : |
534 | 0 | result_high = 14; |
535 | 0 | break; |
536 | 0 | case 'F' : |
537 | 0 | case 'f' : |
538 | 0 | result_high = 15; |
539 | 0 | break; |
540 | 0 | } |
541 | 0 | switch (str[1]) |
542 | 0 | { |
543 | 0 | case '0' : |
544 | 0 | result_low = 0; |
545 | 0 | break; |
546 | 0 | case '1' : |
547 | 0 | result_low = 1; |
548 | 0 | break; |
549 | 0 | case '2' : |
550 | 0 | result_low = 2; |
551 | 0 | break; |
552 | 0 | case '3' : |
553 | 0 | result_low = 3; |
554 | 0 | break; |
555 | 0 | case '4' : |
556 | 0 | result_low = 4; |
557 | 0 | break; |
558 | 0 | case '5' : |
559 | 0 | result_low = 5; |
560 | 0 | break; |
561 | 0 | case '6' : |
562 | 0 | result_low = 6; |
563 | 0 | break; |
564 | 0 | case '7' : |
565 | 0 | result_low = 7; |
566 | 0 | break; |
567 | 0 | case '8' : |
568 | 0 | result_low = 8; |
569 | 0 | break; |
570 | 0 | case '9' : |
571 | 0 | result_low = 9; |
572 | 0 | break; |
573 | 0 | case 'A' : |
574 | 0 | case 'a' : |
575 | 0 | result_low = 10; |
576 | 0 | break; |
577 | 0 | case 'B' : |
578 | 0 | case 'b' : |
579 | 0 | result_low = 11; |
580 | 0 | break; |
581 | 0 | case 'C' : |
582 | 0 | case 'c' : |
583 | 0 | result_low = 12; |
584 | 0 | break; |
585 | 0 | case 'D' : |
586 | 0 | case 'd' : |
587 | 0 | result_low = 13; |
588 | 0 | break; |
589 | 0 | case 'E' : |
590 | 0 | case 'e' : |
591 | 0 | result_low = 14; |
592 | 0 | break; |
593 | 0 | case 'F' : |
594 | 0 | case 'f' : |
595 | 0 | result_low = 15; |
596 | 0 | break; |
597 | 0 | } |
598 | 0 | return (uint8_t) ((result_high<<4) + result_low); |
599 | 0 | } |
600 | | |
601 | | |
602 | | /** |
603 | | * Given one byte, populate result with two byte representing |
604 | | * the hex number. |
605 | | * |
606 | | * Ie. deparse_hex( 255, mystr) |
607 | | * -> mystr[0] = 'F' and mystr[1] = 'F' |
608 | | * |
609 | | * No error checking done |
610 | | */ |
611 | | void |
612 | | deparse_hex(uint8_t str, char *result) |
613 | 0 | { |
614 | 0 | int input_high; |
615 | 0 | int input_low; |
616 | 0 | static char outchr[]= |
617 | 0 | { |
618 | 0 | "0123456789ABCDEF" |
619 | 0 | }; |
620 | |
|
621 | 0 | input_high = (str>>4); |
622 | 0 | input_low = (str & 0x0F); |
623 | |
|
624 | 0 | result[0] = outchr[input_high]; |
625 | 0 | result[1] = outchr[input_low]; |
626 | |
|
627 | 0 | } |
628 | | |
629 | | |
630 | | /** |
631 | | * Find interpolation point I |
632 | | * between point A and point B |
633 | | * so that the len(AI) == len(AB)*F |
634 | | * and I falls on AB segment. |
635 | | * |
636 | | * Example: |
637 | | * |
638 | | * F=0.5 : A----I----B |
639 | | * F=1 : A---------B==I |
640 | | * F=0 : A==I---------B |
641 | | * F=.2 : A-I-------B |
642 | | */ |
643 | | void |
644 | | interpolate_point4d(const POINT4D *A, const POINT4D *B, POINT4D *I, double F) |
645 | 0 | { |
646 | 0 | assert(F >= 0 && F <= 1); |
647 | 0 | I->x=A->x+((B->x-A->x)*F); |
648 | 0 | I->y=A->y+((B->y-A->y)*F); |
649 | 0 | I->z=A->z+((B->z-A->z)*F); |
650 | 0 | I->m=A->m+((B->m-A->m)*F); |
651 | 0 | } |
652 | | |
653 | | |
654 | | int _lwgeom_interrupt_requested = 0; |
655 | | void |
656 | 0 | lwgeom_request_interrupt(void) { |
657 | 0 | _lwgeom_interrupt_requested = 1; |
658 | 0 | } |
659 | | void |
660 | 0 | lwgeom_cancel_interrupt(void) { |
661 | 0 | _lwgeom_interrupt_requested = 0; |
662 | 0 | } |
663 | | |
664 | | lwinterrupt_callback *_lwgeom_interrupt_callback = 0; |
665 | | lwinterrupt_callback * |
666 | 0 | lwgeom_register_interrupt_callback(lwinterrupt_callback *cb) { |
667 | 0 | lwinterrupt_callback *old = _lwgeom_interrupt_callback; |
668 | 0 | _lwgeom_interrupt_callback = cb; |
669 | 0 | return old; |
670 | 0 | } |