GRASS GIS 8 Programmer's Manual 8.4.1(2025)-45ca3179ab
Loading...
Searching...
No Matches
view.c
Go to the documentation of this file.
1/*!
2 * \file lib/gis/view.c
3 *
4 * \brief GIS Library - 3D View functions.
5 *
6 * (C) 2001-2014 by the GRASS Development Team
7 *
8 * This program is free software under the GNU General Public License
9 * (>=v2). Read the file COPYING that comes with GRASS for details.
10 *
11 * \author Bill Brown - US Army CERL
12 *
13 * \date 1992-2008
14 */
15
16#include <stdio.h>
17#include <string.h>
18#include <stdlib.h>
19#include <grass/gis.h>
20#include <grass/glocale.h>
21
22#define REQ_KEYS 8
23
24static int compare_wind(const struct Cell_head *, const struct Cell_head *);
25static int get_bool(const char *);
26static void pr_winerr(int, const char *);
27static void edge_sort(float sides[4]);
28static int read_old_format(struct G_3dview *, FILE *);
29
30static const int vers_major = 4;
31static const int vers_minor = 1;
32
33static int Suppress_warn = 0;
34
35/**
36 * \brief Turns 3D View warnings on and off.
37 *
38 * If Suppress_warn is 0, a warning will be printed if less than 95% of
39 * the window when the view was saved overlaps the current window.
40 *
41 * \param[in] b
42 * \return
43 */
44
46{
47 Suppress_warn = b ? 0 : 1;
48}
49
50/**
51 * \brief Sets default for <b>v</b> based on <b>w</b>.
52 *
53 * \param[in,out] v
54 * \param[in] w
55 * \return always returns 1
56 */
57
58int G_get_3dview_defaults(struct G_3dview *v, struct Cell_head *w)
59{
60 if (!v || !w)
61 return (-1);
62
63 v->exag = 1.0;
64 v->fov = 40.0;
65 v->from_to[0][0] = (w->east + w->west) / 2.0;
66 v->from_to[0][1] = w->south - (w->north - w->south);
67 v->from_to[0][2] = w->north - w->south;
68 v->from_to[1][0] = (w->east + w->west) / 2.0;
69 v->from_to[1][1] = (w->north + w->south) / 2.0;
70 v->from_to[1][2] = 0.0;
71
72 v->twist = 0.0;
73 v->mesh_freq = 15;
74 v->poly_freq = 1;
75 v->display_type = 2;
76 v->colorgrid = v->fringe = v->surfonly = v->lightson = v->doavg = 0;
77 v->dozero = v->shading = 1;
78 strcpy(v->bg_col, "black");
79 strcpy(v->grid_col, "white");
80 strcpy(v->other_col, "red");
81 v->ambient = v->shine = 0.3;
82 v->lightcol[0] = v->lightcol[1] = v->lightcol[2] = 0.8;
83 v->lightpos[0] = w->west;
84 v->lightpos[1] = w->north;
85 v->lightpos[2] = (w->east - w->west) / 2.0;
86 v->lightpos[3] = 1.0; /* local source */
87
88 v->vwin.north = w->north;
89 v->vwin.south = w->south;
90 v->vwin.east = w->east;
91 v->vwin.west = w->west;
92 v->vwin.format = w->format;
93 v->vwin.compressed = w->compressed;
94 v->vwin.proj = w->proj;
95 v->vwin.zone = w->zone;
96 v->vwin.ew_res = w->ew_res;
97 v->vwin.ns_res = w->ns_res;
98 v->vwin.cols = w->cols;
99 v->vwin.rows = w->rows;
100
101 return (1);
102}
103
104/**
105 * \brief Saves info to a 3d.view file in the current mapset.
106 *
107 * The address of a window (struct Cell_head *) may be passed, or if
108 * NULL is passed, the Cell_head structure inside the G_3dview struct
109 * will be used. e.g., if you called <i>G_get_3dview_defaults</i> with
110 * the Cell_head you want saved, the G_3dview returned already contains
111 * the new Cell_head. But if you're using all the keywords, so didn't
112 * need defaults, pass this routine the address of a Cell_head.<br>
113 *
114 * User should call <i>G_get_3dview_defaults</i> before filling a
115 * G_3dview struct to be written if not using all of the optional
116 * keywords.<br>
117 *
118 * These keywords are constant in all 3d.view files:<br>
119 * PGM_ID<br>
120 * <b>cell keywords:</b><br>
121 * north<br>
122 * south<br>
123 * east<br>
124 * west<br>
125 * rows<br>
126 * cols<br>
127 * <b>required keywords:</b><br>
128 * TO_EASTING<br>
129 * TO_NORTHING<br>
130 * TO_HEIGHT<br>
131 * FROM_EASTING<br>
132 * FROM_NORTHING<br>
133 * FROM_HEIGHT<br>
134 * Z_EXAG<br>
135 * FIELD_VIEW<br>
136 * <b>optional keywords:</b> (defaults provided when reading)<br>
137 * TWIST<br>
138 * MESH_FREQ<br>
139 * POLY_RES<br>
140 * DOAVG<br>
141 * DISPLAY_TYPE<br>
142 * DOZERO<br>
143 * COLORGRID<br>
144 * SHADING<br>
145 * FRINGE<br>
146 * BG_COL<br>
147 * GRID_COL<br>
148 * OTHER_COL<br>
149 * LIGHTS_ON<br>
150 * LIGHTPOS<br>
151 * LIGHTCOL<br>
152 * LIGHTAMBIENT<br>
153 * SHINE<br>
154 * SURFACEONLY<br>
155 *
156 * \param[in] fname file name
157 * \param[in] View
158 * \param[in] Win
159 * \return 1 on success
160 * \return -1 on error
161 */
162
163int G_put_3dview(const char *fname, const struct G_3dview *View,
164 const struct Cell_head *Win)
165{
166 FILE *fp;
167
168 if (NULL == (fp = G_fopen_new("3d.view", fname))) {
169 G_warning(_("Unable to open %s for writing"), fname);
170 return (-1);
171 }
172
173 fprintf(fp, "# %01d.%02d\n", vers_major, vers_minor);
174 fprintf(fp, "PGM_ID: %s\n", View->pgm_id);
175
176 if (Win) {
177 fprintf(fp, "north: %f\n", Win->north);
178 fprintf(fp, "south: %f\n", Win->south);
179 fprintf(fp, "east: %f\n", Win->east);
180 fprintf(fp, "west: %f\n", Win->west);
181 fprintf(fp, "rows: %d\n", Win->rows);
182 fprintf(fp, "cols: %d\n", Win->cols);
183 }
184 else {
185 fprintf(fp, "north: %f\n", View->vwin.north);
186 fprintf(fp, "south: %f\n", View->vwin.south);
187 fprintf(fp, "east: %f\n", View->vwin.east);
188 fprintf(fp, "west: %f\n", View->vwin.west);
189 fprintf(fp, "rows: %d\n", View->vwin.rows);
190 fprintf(fp, "cols: %d\n", View->vwin.cols);
191 }
192
193 fprintf(fp, "TO_EASTING: %f\n", View->from_to[1][0]);
194 fprintf(fp, "TO_NORTHING: %f\n", View->from_to[1][1]);
195 fprintf(fp, "TO_HEIGHT: %f\n", View->from_to[1][2]);
196 fprintf(fp, "FROM_EASTING: %f\n", View->from_to[0][0]);
197 fprintf(fp, "FROM_NORTHING: %f\n", View->from_to[0][1]);
198 fprintf(fp, "FROM_HEIGHT: %f\n", View->from_to[0][2]);
199 fprintf(fp, "Z_EXAG: %f\n", View->exag);
200 fprintf(fp, "TWIST: %f\n", View->twist);
201 fprintf(fp, "FIELD_VIEW: %f\n", View->fov);
202 fprintf(fp, "MESH_FREQ: %d\n", View->mesh_freq);
203 fprintf(fp, "POLY_RES: %d\n", View->poly_freq);
204 fprintf(fp, "DOAVG: %d\n", View->doavg);
205 fprintf(fp, "DISPLAY_TYPE: %d\n", View->display_type);
206 fprintf(fp, "DOZERO: %d\n", View->dozero);
207
208 fprintf(fp, "COLORGRID: %d\n", View->colorgrid); /* 1 = use color */
209 fprintf(fp, "SHADING: %d\n", View->shading);
210 fprintf(fp, "FRINGE: %d\n", View->fringe);
211 fprintf(fp, "BG_COL: %s\n", View->bg_col);
212 fprintf(fp, "GRID_COL: %s\n", View->grid_col);
213 fprintf(fp, "OTHER_COL: %s\n", View->other_col);
214 fprintf(fp, "SURFACEONLY: %d\n", View->surfonly);
215 fprintf(fp, "LIGHTS_ON: %d\n", View->lightson);
216 fprintf(fp, "LIGHTPOS: %f %f %f %f\n", View->lightpos[0], View->lightpos[1],
217 View->lightpos[2], View->lightpos[3]);
218 fprintf(fp, "LIGHTCOL: %f %f %f\n", View->lightcol[0], View->lightcol[1],
219 View->lightcol[2]);
220 fprintf(fp, "LIGHTAMBIENT: %f\n", View->ambient);
221 fprintf(fp, "SHINE: %f\n", View->shine);
222
223 fclose(fp);
224
225 return (1);
226}
227
228/**
229 * \brief Gets a 3D View.
230 *
231 * If reading an old format, the window boundaries are not checked
232 * against the current window since boundaries weren't saved.
233 *
234 * \param[in] fname
235 * \param[in] mapset
236 * \param[in,out] View
237 * \return -1 on error
238 * \return 1 on success
239 * \return 2 if <b>fname</b> was written with this version of routine
240 * \return 0 if is older format (through 4.0)
241 */
242
243int G_get_3dview(const char *fname, const char *mapset, struct G_3dview *View)
244{
245 struct Cell_head curwin;
246 FILE *fp;
247 char buffer[80], keystring[24], boo[8], nbuf[128], ebuf[128];
248 int lap, v_maj, v_min, wind_keys = 0, reqkeys = 0;
249 int current = 0; /* current version flag */
250
251 mapset = G_find_file2("3d.view", fname, mapset);
252 if (mapset != NULL) {
253 if (NULL == (fp = G_fopen_old("3d.view", fname, mapset))) {
254 G_warning(_("Unable to open %s for reading"), fname);
255 return (-1);
256 }
257
258 G_get_set_window(&curwin);
259 G_get_3dview_defaults(View, &curwin);
260
261 if (NULL != fgets(buffer, 80, fp)) {
262 if (buffer[0] != '#') { /* old d.3d format */
263 rewind(fp);
264 if (0 <= read_old_format(View, fp))
265 return (0);
266 else
267 return (-1);
268 }
269 else {
270 sscanf(buffer, "#%d.%d\n", &v_maj, &v_min);
271 if (v_maj == vers_major && v_min == vers_minor)
272 current = 1; /* same version */
273 }
274 }
275
276 while (NULL != fgets(buffer, 75, fp)) {
277 if (buffer[0] != '#') {
278
279 sscanf(buffer, "%[^:]:", keystring);
280
281 if (!strcmp(keystring, "PGM_ID")) {
282 sscanf(buffer, "%*s%s", (View->pgm_id));
283 continue;
284 }
285 if (!strcmp(keystring, "north")) {
286 sscanf(buffer, "%*s%lf", &(View->vwin.north));
287 ++wind_keys;
288 continue;
289 }
290 if (!strcmp(keystring, "south")) {
291 sscanf(buffer, "%*s%lf", &(View->vwin.south));
292 ++wind_keys;
293 continue;
294 }
295 if (!strcmp(keystring, "east")) {
296 sscanf(buffer, "%*s%lf", &(View->vwin.east));
297 ++wind_keys;
298 continue;
299 }
300 if (!strcmp(keystring, "west")) {
301 sscanf(buffer, "%*s%lf", &(View->vwin.west));
302 ++wind_keys;
303 continue;
304 }
305 if (!strcmp(keystring, "rows")) {
306 sscanf(buffer, "%*s%d", &(View->vwin.rows));
307 ++wind_keys;
308 continue;
309 }
310 if (!strcmp(keystring, "cols")) {
311 sscanf(buffer, "%*s%d", &(View->vwin.cols));
312 ++wind_keys;
313 continue;
314 }
315 if (!strcmp(keystring, "TO_EASTING")) {
316 sscanf(buffer, "%*s%f", &(View->from_to[1][0]));
317 ++reqkeys;
318 continue;
319 }
320 if (!strcmp(keystring, "TO_NORTHING")) {
321 sscanf(buffer, "%*s%f", &(View->from_to[1][1]));
322 ++reqkeys;
323 continue;
324 }
325 if (!strcmp(keystring, "TO_HEIGHT")) {
326 sscanf(buffer, "%*s%f", &(View->from_to[1][2]));
327 ++reqkeys;
328 continue;
329 }
330 if (!strcmp(keystring, "FROM_EASTING")) {
331 sscanf(buffer, "%*s%f", &(View->from_to[0][0]));
332 ++reqkeys;
333 continue;
334 }
335 if (!strcmp(keystring, "FROM_NORTHING")) {
336 sscanf(buffer, "%*s%f", &(View->from_to[0][1]));
337 ++reqkeys;
338 continue;
339 }
340 if (!strcmp(keystring, "FROM_HEIGHT")) {
341 sscanf(buffer, "%*s%f", &(View->from_to[0][2]));
342 ++reqkeys;
343 continue;
344 }
345 if (!strcmp(keystring, "Z_EXAG")) {
346 sscanf(buffer, "%*s%f", &(View->exag));
347 ++reqkeys;
348 continue;
349 }
350 if (!strcmp(keystring, "MESH_FREQ")) {
351 sscanf(buffer, "%*s%d", &(View->mesh_freq));
352 continue;
353 }
354 if (!strcmp(keystring, "POLY_RES")) {
355 sscanf(buffer, "%*s%d", &(View->poly_freq));
356 continue;
357 }
358 if (!strcmp(keystring, "DOAVG")) {
359 sscanf(buffer, "%*s%d", &(View->doavg));
360 continue;
361 }
362 if (!strcmp(keystring, "FIELD_VIEW")) {
363 sscanf(buffer, "%*s%f", &(View->fov));
364 ++reqkeys;
365 continue;
366 }
367 if (!strcmp(keystring, "TWIST")) {
368 sscanf(buffer, "%*s%f", &(View->twist));
369 continue;
370 }
371 if (!strcmp(keystring, "DISPLAY_TYPE")) {
372 sscanf(buffer, "%*s%d", &View->display_type);
373 continue;
374 }
375 if (!strcmp(keystring, "DOZERO")) {
376 sscanf(buffer, "%*s%s", boo);
377 View->dozero = get_bool(boo);
378 continue;
379 }
380 if (!strcmp(keystring, "COLORGRID")) {
381 sscanf(buffer, "%*s%s", boo);
382 View->colorgrid = get_bool(boo);
383 continue;
384 }
385 if (!strcmp(keystring, "FRINGE")) {
386 sscanf(buffer, "%*s%s", boo);
387 View->fringe = get_bool(boo);
388 continue;
389 }
390 if (!strcmp(keystring, "SHADING")) {
391 sscanf(buffer, "%*s%s", boo);
392 View->shading = get_bool(boo);
393 continue;
394 }
395 if (!strcmp(keystring, "BG_COL")) {
396 sscanf(buffer, "%*s%s", View->bg_col);
397 continue;
398 }
399 if (!strcmp(keystring, "GRID_COL")) {
400 sscanf(buffer, "%*s%s", View->grid_col);
401 continue;
402 }
403 if (!strcmp(keystring, "OTHER_COL")) {
404 sscanf(buffer, "%*s%s", View->other_col);
405 continue;
406 }
407 if (!strcmp(keystring, "SURFACEONLY")) {
408 sscanf(buffer, "%*s%s", boo);
409 View->surfonly = get_bool(boo);
410 continue;
411 }
412 if (!strcmp(keystring, "LIGHTS_ON")) {
413 sscanf(buffer, "%*s%s", boo);
414 View->lightson = get_bool(boo);
415 continue;
416 }
417 if (!strcmp(keystring, "LIGHTPOS")) {
418 sscanf(buffer, "%*s%f%f%f%f", &(View->lightpos[0]),
419 &(View->lightpos[1]), &(View->lightpos[2]),
420 &(View->lightpos[3]));
421 continue;
422 }
423 if (!strcmp(keystring, "LIGHTCOL")) {
424 sscanf(buffer, "%*s%f%f%f", &(View->lightcol[0]),
425 &(View->lightcol[1]), &(View->lightcol[2]));
426 continue;
427 }
428 if (!strcmp(keystring, "LIGHTAMBIENT")) {
429 sscanf(buffer, "%*s%f", &(View->ambient));
430 continue;
431 }
432 if (!strcmp(keystring, "SHINE")) {
433 sscanf(buffer, "%*s%f", &(View->shine));
434 continue;
435 }
436 }
437 }
438
439 fclose(fp);
440
441 if (reqkeys != REQ_KEYS) /* required keys not found */
442 return (-1);
443
444 /* fill rest of View->vwin */
445 if (wind_keys == 6) {
446 View->vwin.ew_res =
447 (View->vwin.east - View->vwin.west) / View->vwin.cols;
448 View->vwin.ns_res =
449 (View->vwin.north - View->vwin.south) / View->vwin.rows;
450 }
451 else
452 return (0); /* older format */
453
454 if (!Suppress_warn) {
455 if (95 > (lap = compare_wind(&(View->vwin), &curwin))) {
456
457 fprintf(stderr, _("GRASS window when view was saved:\n"));
458 G_format_northing(View->vwin.north, nbuf, G_projection());
459 fprintf(stderr, "north: %s\n", nbuf);
460 G_format_northing(View->vwin.south, nbuf, G_projection());
461 fprintf(stderr, "south: %s\n", nbuf);
462 G_format_easting(View->vwin.east, ebuf, G_projection());
463 fprintf(stderr, "east: %s\n", ebuf);
464 G_format_easting(View->vwin.west, ebuf, G_projection());
465 fprintf(stderr, "west: %s\n", ebuf);
466 pr_winerr(lap, fname);
467 }
468 }
469 }
470 else {
471 G_warning(_("Unable to open %s for reading"), fname);
472 return (-1);
473 }
474
475 if (current)
476 return (2);
477
478 return (1);
479}
480
481/* returns the percentage of savedwin that overlaps curwin */
482
483static int compare_wind(const struct Cell_head *savedwin,
484 const struct Cell_head *curwin)
485{
486 float e_ings[4], n_ings[4], area_lap, area_saved;
487 int outside = 0;
488
489 if (savedwin->north < curwin->south)
490 outside = 1;
491 if (savedwin->south > curwin->north)
492 outside = 1;
493 if (savedwin->east < curwin->west)
494 outside = 1;
495 if (savedwin->west > curwin->east)
496 outside = 1;
497 if (outside)
498 return (0);
499
500 e_ings[0] = savedwin->west;
501 e_ings[1] = savedwin->east;
502 e_ings[2] = curwin->west;
503 e_ings[3] = curwin->east;
504 edge_sort(e_ings);
505
506 n_ings[0] = savedwin->south;
507 n_ings[1] = savedwin->north;
508 n_ings[2] = curwin->south;
509 n_ings[3] = curwin->north;
510 edge_sort(n_ings);
511
512 area_lap = (e_ings[2] - e_ings[1]) * (n_ings[2] - n_ings[1]);
513 area_saved =
514 (savedwin->east - savedwin->west) * (savedwin->north - savedwin->south);
515
516 return ((int)(area_lap * 100.0 / area_saved));
517}
518
519static int get_bool(const char *str)
520{
521 if (str[0] == 'y' || str[0] == 'Y')
522 return (1);
523 if (str[0] == 'n' || str[0] == 'N')
524 return (0);
525
526 return (atoi(str) ? 1 : 0);
527}
528
529static void
530pr_winerr(int vis, /* % of saved window overlapping current window */
531 const char *viewname)
532{
533 switch (vis) {
534 case 0:
535 G_warning(_(" Window saved in \"%s\" is completely outside of current "
536 "GRASS window."),
537 viewname);
538 break;
539 default:
540 G_warning(_(" Only %d%% of window saved in \"%s\" overlaps with "
541 "current GRASS window."),
542 vis, viewname);
543 break;
544 }
545}
546
547/*********************************************************************/
548/* sorts 4 floats from lowest to highest */
549
550static void edge_sort(float sides[4])
551{
552 int i, j;
553 float temp;
554
555 for (i = 0; i < 4; ++i) {
556 for (j = i + 1; j < 4; ++j) {
557 if (sides[j] < sides[i]) { /* then swap */
558 temp = sides[i];
559 sides[i] = sides[j];
560 sides[j] = temp;
561 }
562 }
563 }
564}
565
566static int read_old_format(struct G_3dview *v, FILE *fp)
567{
568 char buffer[80];
569 int req_keys = 0;
570 double td;
571 char boo[8];
572
573 strcpy((v->pgm_id), "d.3d");
574 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->from_to[1][0])))
575 ++req_keys;
576 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->from_to[1][1])))
577 ++req_keys;
578 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->from_to[1][2])))
579 ++req_keys;
580 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->from_to[0][0])))
581 ++req_keys;
582 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->from_to[0][1])))
583 ++req_keys;
584 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->from_to[0][2])))
585 ++req_keys;
586 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->exag)))
587 ++req_keys;
588 sscanf(fgets(buffer, 80, fp), "%d", &(v->mesh_freq));
589 if (1 == sscanf(fgets(buffer, 80, fp), "%f", &(v->fov)))
590 ++req_keys;
591 if (1 == sscanf(fgets(buffer, 80, fp), "%lf", &td)) { /* resolution */
592 v->vwin.rows = (v->vwin.north - v->vwin.south) / td;
593 v->vwin.cols = (v->vwin.east - v->vwin.west) / td;
594 v->vwin.ew_res = v->vwin.ns_res = td;
595 }
596
597 sscanf(fgets(buffer, 80, fp), "%s", boo); /* linesonly */
598 v->display_type = get_bool(boo) ? 1 : 3;
599 sscanf(fgets(buffer, 80, fp), "%s", boo);
600 v->dozero = get_bool(boo);
601 sscanf(fgets(buffer, 80, fp), "%s", v->grid_col);
602 if (!strcmp(v->grid_col, "color"))
603 v->colorgrid = 1;
604
605 sscanf(fgets(buffer, 80, fp), "%s", v->other_col);
606 sscanf(fgets(buffer, 80, fp), "%s", v->bg_col);
607 sscanf(fgets(buffer, 80, fp), "%s", boo);
608 v->doavg = get_bool(boo);
609
610 if (v->exag) { /* old 3d.view files saved height with no exag */
611 v->from_to[0][2] /= v->exag;
612 v->from_to[1][2] /= v->exag;
613 }
614
615 fclose(fp);
616 if (req_keys == REQ_KEYS)
617 return (1);
618 else
619 return (-1);
620}
#define NULL
Definition ccmath.h:32
double b
const char * G_find_file2(const char *element, const char *name, const char *mapset)
Searches for a file from the mapset search list or in a specified mapset. (look but don't touch)
Definition find_file.c:234
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition gis/error.c:203
FILE * G_fopen_old(const char *element, const char *name, const char *mapset)
Open a database file for reading.
Definition gis/open.c:251
FILE * G_fopen_new(const char *element, const char *name)
Open a new database file.
Definition gis/open.c:219
void G_get_set_window(struct Cell_head *window)
Get the current working window (region)
#define strcpy
Definition parson.c:62
int G_projection(void)
Query cartographic projection.
Definition proj1.c:32
int G_get_3dview(const char *fname, const char *mapset, struct G_3dview *View)
Gets a 3D View.
Definition view.c:243
void G_3dview_warning(int b)
Turns 3D View warnings on and off.
Definition view.c:45
int G_get_3dview_defaults(struct G_3dview *v, struct Cell_head *w)
Sets default for v based on w.
Definition view.c:58
int G_put_3dview(const char *fname, const struct G_3dview *View, const struct Cell_head *Win)
Saves info to a 3d.view file in the current mapset.
Definition view.c:163
#define REQ_KEYS
Definition view.c:22
void G_format_northing(double north, char *buf, int projection)
Northing to ASCII.
Definition wind_format.c:29
void G_format_easting(double east, char *buf, int projection)
Easting to ASCII.
Definition wind_format.c:49