source: git/src/extend.c @ 5318008

stereo-2025
Last change on this file since 5318008 was 36b8060, checked in by Olly Betts <olly@…>, 4 months ago

Eliminate fopenWithPthAndExt() use in img.c

Now we have img_read_stream_survey() we can easily move this handling
out of img.c and into Survex-specific code.

Making this change revealed that the filename_opened member of
the img struct is only ever set to NULL when used outside of Survex
and set but never actually used inside of Survex, so remove this
member and document that it was always NULL.

  • Property mode set to 100644
File size: 22.1 KB
Line 
1/* extend.c
2 * Produce an extended elevation
3 * Copyright (C) 1995-2025 Olly Betts
4 * Copyright (C) 2004,2005 John Pybus
5 *
6 * This program 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 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21#include <config.h>
22
23#include <float.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "cmdline.h"
29#include "debug.h"
30#include "filelist.h"
31#include "filename.h"
32#include "hash.h"
33#include "img_hosted.h"
34#include "message.h"
35#include "osalloc.h"
36#include "useful.h"
37
38/* To save memory we should probably use the prefix hash for the prefix on
39 * point labels (FIXME) */
40
41typedef struct stn {
42   const char *label;
43   int flags;
44   const struct stn *next;
45} stn;
46
47typedef struct splay {
48   struct POINT *pt;
49   struct splay *next;
50} splay;
51
52typedef struct POINT {
53   img_point p;
54   double X;
55   const stn *stns;
56   unsigned int order;
57   char dir;
58   char fDone;
59   char fBroken;
60   splay *splays;
61   struct POINT *next;
62} point;
63
64typedef struct LEG {
65   point *fr, *to;
66   const char *prefix;
67   char dir;
68   char fDone;
69   char broken;
70   int flags;
71   struct LEG *next;
72} leg;
73
74/* Values for leg.broken: */
75#define BREAK_FR    0x01
76#define BREAK_TO    0x02
77
78/* Values for point.dir and leg.dir: */
79#define ELEFT  0x01
80#define ERIGHT 0x02
81#define ESWAP  0x04
82
83static point headpoint = {{0, 0, 0}, 0, NULL, 0, 0, 0, 0, NULL, NULL};
84
85static leg headleg = {NULL, NULL, NULL, 0, 0, 0, 0, NULL};
86
87static img *pimg_out;
88
89static bool show_breaks = false;
90
91static void do_stn(point *, double, const char *, int, int, double, double);
92
93typedef struct pfx {
94   const char *label;
95   struct pfx *next;
96} pfx;
97
98static pfx **htab;
99
100#define HTAB_SIZE 0x2000
101
102static const char *
103find_prefix(const char *prefix)
104{
105   pfx *p;
106
107   SVX_ASSERT(prefix);
108
109   unsigned hash = hash_string(prefix) & (HTAB_SIZE - 1);
110   for (p = htab[hash]; p; p = p->next) {
111      if (strcmp(prefix, p->label) == 0) return p->label;
112   }
113
114   p = osnew(pfx);
115   p->label = osstrdup(prefix);
116   p->next = htab[hash];
117   htab[hash] = p;
118
119   return p->label;
120}
121
122static point *
123find_point(const img_point *pt)
124{
125   point *p;
126   for (p = headpoint.next; p != NULL; p = p->next) {
127      if (pt->x == p->p.x && pt->y == p->p.y && pt->z == p->p.z) {
128         return p;
129      }
130   }
131
132   p = osmalloc(ossizeof(point));
133   p->p = *pt;
134   p->X = HUGE_VAL;
135   p->stns = NULL;
136   p->order = 0;
137   p->dir = 0;
138   p->fDone = 0;
139   p->fBroken = 0;
140   p->splays = NULL;
141   p->next = headpoint.next;
142   headpoint.next = p;
143   return p;
144}
145
146static void
147add_leg(point *fr, point *to, const char *prefix, int flags)
148{
149   leg *l;
150   fr->order++;
151   to->order++;
152   l = osmalloc(ossizeof(leg));
153   l->fr = fr;
154   l->to = to;
155   if (prefix)
156      l->prefix = find_prefix(prefix);
157   else
158      l->prefix = NULL;
159   l->next = headleg.next;
160   l->dir = 0;
161   l->fDone = 0;
162   l->broken = 0;
163   l->flags = flags;
164   headleg.next = l;
165}
166
167static void
168add_label(point *p, const char *label, int flags)
169{
170   stn *s = osnew(stn);
171   s->label = osstrdup(label);
172   s->flags = flags;
173   s->next = p->stns;
174   p->stns = s;
175}
176
177/* Read in config file */
178
179
180/* lifted from img.c Should be put somewhere common? JPNP*/
181static char *
182getline_alloc(FILE *fh, size_t ilen)
183{
184   int ch;
185   size_t i = 0;
186   size_t len = ilen;
187   char *buf = xosmalloc(len);
188   if (!buf) return NULL;
189
190   ch = GETC(fh);
191   while (ch != '\n' && ch != '\r' && ch != EOF) {
192      buf[i++] = ch;
193      if (i == len - 1) {
194         char *p;
195         len += len;
196         p = xosrealloc(buf, len);
197         if (!p) {
198            osfree(buf);
199            return NULL;
200         }
201         buf = p;
202      }
203      ch = GETC(fh);
204   }
205   if (ch == '\n' || ch == '\r') {
206      int otherone = ch ^ ('\n' ^ '\r');
207      ch = GETC(fh);
208      /* if it's not the other eol character, put it back */
209      if (ch != otherone) ungetc(ch, fh);
210   }
211   buf[i++] = '\0';
212   return buf;
213}
214
215static int lineno = 0;
216static point *start = NULL;
217
218static char*
219delimword(char *ln, char** lr)
220{
221   char *le;
222
223   while (*ln == ' ' || *ln == '\t' || *ln == '\n' || *ln == '\r')
224      ln++;
225
226   le = ln;
227   while (*le != ' ' && *le != '\t' && *le != '\n' && *le != '\r' && *le != ';' && *le != '\0')
228      le++;
229
230   if (*le == '\0' || *le == ';') {
231      *lr = le;
232   } else {
233      *lr = le + 1;
234   }
235
236   *le = '\0';
237   return ln;
238}
239
240static const char*
241check_label(const char* label, const char* ll, const char* ln)
242{
243    if (strcmp(label, ll) == 0) {
244        return ln;
245    } else if (strcmp(label, ln) == 0) {
246        return ll;
247    } else {
248        return NULL;
249    }
250}
251
252static void
253parseconfigline(const char *fnm, char *ln)
254{
255   point *p;
256   const stn *s;
257   const stn *t;
258   leg *l;
259   char *lc = NULL;
260
261   ln = delimword(ln, &lc);
262
263   if (*ln == '\0') return;
264
265   if (strcmp(ln, "*start")==0) {
266      ln = delimword(lc, &lc);
267      if (*ln == 0)
268         /* TRANSLATORS: Here "station" is a survey station, not a train station. */
269         fatalerror_in_file(fnm, lineno, /*Expecting station name*/28);
270      for (p = headpoint.next; p != NULL; p = p->next) {
271         for (s = p->stns; s; s = s->next) {
272            if (strcmp(s->label, ln)==0) {
273               start = p;
274               /* TRANSLATORS: for extend: "extend" is starting to produce an extended elevation from station %s */
275               printf(msg(/*Starting from station %s*/512),ln);
276               putnl();
277               goto loopend;
278            }
279         }
280      }
281      /* TRANSLATORS: for extend: the user specified breaking a loop or
282       * changing extend direction at this station, but we didn’t find it in
283       * the 3d file */
284      warning_in_file(fnm, lineno, /*Failed to find station %s*/510, ln);
285   } else if (strcmp(ln, "*eleft")==0) {
286      char *ll = delimword(lc, &lc);
287      if (*ll == 0)
288         fatalerror_in_file(fnm, lineno, /*Expecting station name*/28);
289      ln = delimword(lc, &lc);
290      if (*ln == 0) {
291         /* One argument - look for station to switch at. */
292         for (p = headpoint.next; p != NULL; p = p->next) {
293            for (s = p->stns; s; s = s->next) {
294               if (strcmp(s->label, ll)==0) {
295                  /* TRANSLATORS: for extend: */
296                  printf(msg(/*Extending to the left from station %s*/513), ll);
297                  putnl();
298                  p->dir = ELEFT;
299                  goto loopend;
300               }
301            }
302         }
303         warning_in_file(fnm, lineno, /*Failed to find station %s*/510, ll);
304      } else {
305         /* Two arguments - look for a specified leg. */
306         for (l = headleg.next; l; l=l->next) {
307            point * fr = l->fr;
308            point * to = l->to;
309            if (fr && to) {
310               for (s=fr->stns; s; s=s->next) {
311                  const char* lr = check_label(s->label, ll, ln);
312                  if (lr) {
313                     for (t=to->stns; t; t=t->next) {
314                        if (strcmp(t->label,lr)==0) {
315                           /* TRANSLATORS: for extend: */
316                           printf(msg(/*Extending to the left from leg %s → %s*/515), s->label, t->label);
317                           putnl();
318                           l->dir = ELEFT;
319                           goto loopend;
320                        }
321                     }
322                  }
323               }
324            }
325         }
326         /* TRANSLATORS: for extend: the user specified breaking a loop or
327          * changing extend direction at this leg, but we didn’t find it in the
328          * 3d file */
329         warning_in_file(fnm, lineno, /*Failed to find leg %s → %s*/511, ll, ln);
330      }
331   } else if (strcmp(ln, "*eright")==0) {
332      char *ll = delimword(lc, &lc);
333      if (*ll == 0)
334         fatalerror_in_file(fnm, lineno, /*Expecting station name*/28);
335      ln = delimword(lc, &lc);
336      if (*ln == 0) {
337         /* One argument - look for station to switch at. */
338         for (p = headpoint.next; p != NULL; p = p->next) {
339            for (s = p->stns; s; s = s->next) {
340               if (strcmp(s->label, ll)==0) {
341                  /* TRANSLATORS: for extend: */
342                  printf(msg(/*Extending to the right from station %s*/514), ll);
343                  putnl();
344                  p->dir = ERIGHT;
345                  goto loopend;
346               }
347            }
348         }
349         warning_in_file(fnm, lineno, /*Failed to find station %s*/510, ll);
350      } else {
351         /* Two arguments - look for a specified leg. */
352         for (l = headleg.next; l; l=l->next) {
353            point * fr = l->fr;
354            point * to = l->to;
355            if (fr && to) {
356               for (s=fr->stns; s; s=s->next) {
357                  const char* lr = check_label(s->label, ll, ln);
358                  if (lr) {
359                     for (t=to->stns; t; t=t->next) {
360                        if (strcmp(t->label,lr)==0) {
361                           /* TRANSLATORS: for extend: */
362                           printf(msg(/*Extending to the right from leg %s → %s*/516), s->label, t->label);
363                           putnl();
364                           l->dir=ERIGHT;
365                           goto loopend;
366                        }
367                     }
368                  }
369               }
370            }
371         }
372         warning_in_file(fnm, lineno, /*Failed to find leg %s → %s*/511, ll, ln);
373      }
374   } else if (strcmp(ln, "*eswap")==0) {
375      char *ll = delimword(lc, &lc);
376      if (*ll == 0)
377         fatalerror_in_file(fnm, lineno, /*Expecting station name*/28);
378      ln = delimword(lc, &lc);
379      if (*ln == 0) {
380         /* One argument - look for station to switch at. */
381         for (p = headpoint.next; p != NULL; p = p->next) {
382            for (s = p->stns; s; s = s->next) {
383               if (strcmp(s->label, ll)==0) {
384                  /* TRANSLATORS: for extend: */
385                  printf(msg(/*Swapping extend direction from station %s*/519),ll);
386                  putnl();
387                  p->dir = ESWAP;
388                  goto loopend;
389               }
390            }
391         }
392         warning_in_file(fnm, lineno, /*Failed to find station %s*/510, ll);
393      } else {
394         /* Two arguments - look for a specified leg. */
395         for (l = headleg.next; l; l=l->next) {
396            point * fr = l->fr;
397            point * to = l->to;
398            if (fr && to) {
399               for (s=fr->stns; s; s=s->next) {
400                  const char* lr = check_label(s->label, ll, ln);
401                  if (lr) {
402                     for (t=to->stns; t; t=t->next) {
403                        if (strcmp(t->label,lr)==0) {
404                           /* TRANSLATORS: for extend: */
405                           printf(msg(/*Swapping extend direction from leg %s → %s*/520), s->label, t->label);
406                           putnl();
407                           l->dir = ESWAP;
408                           goto loopend;
409                        }
410                     }
411                  }
412               }
413            }
414         }
415         warning_in_file(fnm, lineno, /*Failed to find leg %s → %s*/511, ll, ln);
416      }
417   } else if (strcmp(ln, "*break")==0) {
418      char *ll = delimword(lc, &lc);
419      if (*ll == 0)
420         fatalerror_in_file(fnm, lineno, /*Expecting station name*/28);
421      ln = delimword(lc, &lc);
422      if (*ln == 0) {
423         /* One argument - look for specified station to break at. */
424         for (p = headpoint.next; p != NULL; p = p->next) {
425            for (s = p->stns; s; s = s->next) {
426               if (strcmp(s->label, ll)==0) {
427                  /* TRANSLATORS: for extend: */
428                  printf(msg(/*Breaking survey loop at station %s*/517), ll);
429                  putnl();
430                  p->fBroken = 1;
431                  goto loopend;
432               }
433            }
434         }
435         warning_in_file(fnm, lineno, /*Failed to find station %s*/510, ll);
436      } else {
437         /* Two arguments - look for specified leg and disconnect it at the
438          * first station. */
439         for (l = headleg.next; l; l=l->next) {
440            point * fr = l->fr;
441            point * to = l->to;
442            if (fr && to) {
443               for (s=fr->stns; s; s=s->next) {
444                  const char* lr = check_label(s->label, ll, ln);
445                  if (lr) {
446                     for (t=to->stns; t; t=t->next) {
447                        if (strcmp(t->label,lr)==0) {
448                           /* TRANSLATORS: for extend: */
449                           printf(msg(/*Breaking survey loop at leg %s → %s*/518), s->label, t->label);
450                           putnl();
451                           l->broken = (lr == ll ? BREAK_TO : BREAK_FR);
452                           goto loopend;
453                        }
454                     }
455                  }
456               }
457            }
458         }
459         warning_in_file(fnm, lineno, /*Failed to find leg %s → %s*/511, ll, ln);
460      }
461   } else {
462      fatalerror_in_file(fnm, lineno, /*Unknown command “%s”*/12, ln);
463   }
464 loopend:
465   ln = delimword(lc, &lc);
466   if (*ln != 0) {
467      fatalerror_in_file(fnm, lineno, /*End of line not blank*/15);
468      /* FIXME: give ln as context? */
469   }
470}
471
472static const struct option long_opts[] = {
473   /* const char *name; int has_arg (0 no_argument, 1 required_*, 2 optional_*); int *flag; int val; */
474   {"survey", required_argument, 0, 's'},
475   {"specfile", required_argument, 0, 'p'},
476   {"show-breaks", no_argument, 0, 'b' },
477   {"help", no_argument, 0, HLP_HELP},
478   {"version", no_argument, 0, HLP_VERSION},
479   {0, 0, 0, 0}
480};
481
482#define short_opts "s:p:b"
483
484static struct help_msg help[] = {
485/*                              <-- */
486   {HLP_ENCODELONG(0),        /*only load the sub-survey with this prefix*/199, 0, 0},
487   /* TRANSLATORS: --help output for extend --specfile option */
488   {HLP_ENCODELONG(1),        /*.espec file to control extending*/90, 0, "ESPEC_FILE"},
489   /* TRANSLATORS: --help output for extend --show-breaks option */
490   {HLP_ENCODELONG(2),        /*show breaks with surface survey legs in output*/91, 0, 0},
491   {0, 0, 0, 0}
492};
493
494static point *
495pick_start_stn(void)
496{
497   point * best = NULL;
498   double zMax = -DBL_MAX;
499   point *p;
500
501   /* Start at the highest entrance with some legs attached. */
502   for (p = headpoint.next; p != NULL; p = p->next) {
503      if (p->order > 0 && p->p.z > zMax) {
504         const stn *s;
505         for (s = p->stns; s; s = s->next) {
506            if (s->flags & img_SFLAG_ENTRANCE) {
507               zMax = p->p.z;
508               return p;
509            }
510         }
511      }
512   }
513   if (best) return best;
514
515   /* If no entrances with legs, start at the highest 1-node. */
516   for (p = headpoint.next; p != NULL; p = p->next) {
517      if (p->order == 1 && p->p.z > zMax) {
518         best = p;
519         zMax = p->p.z;
520      }
521   }
522   if (best) return best;
523
524   /* of course we may have no 1-nodes... */
525   for (p = headpoint.next; p != NULL; p = p->next) {
526      if (p->order != 0 && p->p.z > zMax) {
527         best = p;
528         zMax = p->p.z;
529      }
530   }
531   if (best) return best;
532
533   /* There are no legs - just pick the highest station... */
534   for (p = headpoint.next; p != NULL; p = p->next) {
535      if (p->p.z > zMax) {
536         best = p;
537         zMax = p->p.z;
538      }
539   }
540   return best;
541}
542
543int
544main(int argc, char **argv)
545{
546   const char *fnm_in, *fnm_out;
547   char *desc;
548   img_point pt;
549   int result;
550   point *fr = NULL, *to;
551   const char *survey = NULL;
552   const char *specfile = NULL;
553   img *pimg;
554   int xsections = 0, splays = 0;
555
556   msg_init(argv);
557
558   /* TRANSLATORS: Part of extend --help */
559   cmdline_set_syntax_message(/*INPUT_FILE [OUTPUT_3D_FILE]*/267, 0, NULL);
560   cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, 2);
561   while (1) {
562      int opt = cmdline_getopt();
563      if (opt == EOF) break;
564      switch (opt) {
565         case 'b':
566            show_breaks = true;
567            break;
568         case 's':
569            survey = optarg;
570            break;
571         case 'p':
572            specfile = optarg;
573            break;
574      }
575   }
576   fnm_in = argv[optind++];
577   if (argv[optind]) {
578      fnm_out = argv[optind];
579   } else {
580      char * base_in = base_from_fnm(fnm_in);
581      char * base_out = osmalloc(strlen(base_in) + 8);
582      strcpy(base_out, base_in);
583      strcat(base_out, "_extend");
584      fnm_out = add_ext(base_out, EXT_SVX_3D);
585      osfree(base_in);
586      osfree(base_out);
587   }
588
589   /* try to open image file, and check it has correct header */
590   pimg = img_hosted_open_survey(fnm_in, survey);
591   if (pimg == NULL) fatalerror(img_error2msg(img_error()), fnm_in);
592
593   putnl();
594   puts(msg(/*Reading in data - please wait…*/105));
595
596   htab = osmalloc(ossizeof(pfx*) * HTAB_SIZE);
597   {
598       int i;
599       for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
600   }
601
602   do {
603      result = img_read_item(pimg, &pt);
604      switch (result) {
605      case img_MOVE:
606         fr = find_point(&pt);
607         break;
608      case img_LINE:
609         if (!fr) {
610            result = img_BAD;
611            break;
612         }
613         to = find_point(&pt);
614         if (!(pimg->flags & img_FLAG_SURFACE)) {
615            if (pimg->flags & img_FLAG_SPLAY) {
616               ++splays;
617            } else {
618               add_leg(fr, to, pimg->label, pimg->flags);
619            }
620         }
621         fr = to;
622         break;
623      case img_LABEL:
624         to = find_point(&pt);
625         add_label(to, pimg->label, pimg->flags);
626         break;
627      case img_BAD:
628         (void)img_close(pimg);
629         fatalerror(img_error2msg(img_error()), fnm_in);
630         break;
631      case img_XSECT:
632      case img_XSECT_END:
633         ++xsections;
634         break;
635      }
636   } while (result != img_STOP);
637
638   if (splays) {
639      img_rewind(pimg);
640      fr = NULL;
641      do {
642         result = img_read_item(pimg, &pt);
643         switch (result) {
644         case img_MOVE:
645            fr = find_point(&pt);
646            break;
647         case img_LINE:
648            if (!fr) {
649               result = img_BAD;
650               break;
651            }
652            to = find_point(&pt);
653            if (!(pimg->flags & img_FLAG_SURFACE)) {
654               if (pimg->flags & img_FLAG_SPLAY) {
655                  splay *sp = osmalloc(ossizeof(splay));
656                  --splays;
657                  if (fr->order) {
658                     if (to->order == 0) {
659                        sp->pt = to;
660                        sp->next = fr->splays;
661                        fr->splays = sp;
662                     } else {
663                        printf("Splay without a dead end from %s to %s\n", fr->stns->label, to->stns->label);
664                        osfree(sp);
665                     }
666                  } else if (to->order) {
667                     sp->pt = fr;
668                     sp->next = to->splays;
669                     to->splays = sp;
670                  } else {
671                     printf("Isolated splay from %s to %s\n", fr->stns->label, to->stns->label);
672                     osfree(sp);
673                  }
674               }
675            }
676            fr = to;
677            break;
678         }
679      } while (splays && result != img_STOP);
680   }
681
682   desc = osstrdup(pimg->title);
683
684   if (specfile) {
685      FILE *fs = NULL;
686      char *fnm_used;
687      /* TRANSLATORS: for extend: */
688      printf(msg(/*Applying specfile: “%s”*/521), specfile);
689      putnl();
690      fs = fopenWithPthAndExt("", specfile, NULL, "r", &fnm_used);
691      if (fs == NULL) fatalerror(/*Couldn’t open file “%s”*/1, specfile);
692      while (!FEOF(fs)) {
693         char *lbuf = getline_alloc(fs, 32);
694         lineno++;
695         if (!lbuf)
696            fatalerror_in_file(fnm_used, lineno, /*Error reading file*/18);
697         parseconfigline(fnm_used, lbuf);
698         osfree(lbuf);
699      }
700      osfree(fnm_used);
701   }
702
703   if (start == NULL) {
704      /* *start wasn't specified in specfile. */
705      start = pick_start_stn();
706      if (!start) fatalerror(/*No survey data*/43);
707   }
708
709   /* TRANSLATORS: for extend:
710    * Used to tell the user that a file is being written - %s is the filename
711    */
712   printf(msg(/*Writing %s…*/522), fnm_out);
713   putnl();
714   pimg_out = img_open_write(fnm_out, desc, img_FFLAG_EXTENDED);
715
716   /* Only does single connected component currently. */
717   do_stn(start, 0.0, NULL, ERIGHT, 0, 0.0, 0.0);
718
719   if (xsections) {
720      img_rewind(pimg);
721      /* Read ahead on pimg before writing pimg_out so we find out if an
722       * img_XSECT_END comes next. */
723      char * label = NULL;
724      int flags = 0;
725      do {
726         result = img_read_item(pimg, &pt);
727         if (result != img_XSECT && result != img_XSECT_END)
728            continue;
729         --xsections;
730         if (label) {
731            if (result == img_XSECT_END)
732               flags |= img_XFLAG_END;
733            img_write_item(pimg_out, img_XSECT, flags, label, 0, 0, 0);
734            osfree(label);
735            label = NULL;
736         }
737         if (result == img_XSECT) {
738            label = osstrdup(pimg->label);
739            flags = pimg->flags;
740            pimg_out->l = pimg->l;
741            pimg_out->r = pimg->r;
742            pimg_out->u = pimg->u;
743            pimg_out->d = pimg->d;
744         }
745      } while (xsections && result != img_STOP);
746   }
747
748   (void)img_close(pimg);
749
750   if (!img_close(pimg_out)) {
751      (void)remove(fnm_out);
752      fatalerror(img_error2msg(img_error()), fnm_out);
753   }
754
755   return EXIT_SUCCESS;
756}
757
758static int adjust_direction(int dir, int by) {
759    if (by == ESWAP)
760        return dir ^ (ELEFT|ERIGHT);
761    if (by)
762        return by;
763    return dir;
764}
765
766static void
767do_splays(point *p, double X, int dir, double tdx, double tdy)
768{
769   const splay *sp;
770   double a;
771   double C, S;
772
773   if (!p->splays) return;
774
775   if (tdx == 0 && tdy == 0) {
776       /* Two adjacent plumbs, or a pair of legs that exactly cancel. */
777       return;
778   }
779
780   /* Bearing in radians. */
781   a = atan2(tdx, tdy);
782   if (dir == ELEFT) {
783       a = -M_PI_2 - a;
784   } else {
785       a = M_PI_2 - a;
786   }
787   C = cos(a);
788   S = sin(a);
789   for (sp = p->splays; sp; sp = sp->next) {
790      double x = X;
791      double z = p->p.z;
792      img_write_item(pimg_out, img_MOVE, 0, NULL, x, 0, z);
793
794      double dx = sp->pt->p.x - p->p.x;
795      double dy = sp->pt->p.y - p->p.y;
796      double dz = sp->pt->p.z - p->p.z;
797
798      double tmp = dx * C + dy * S;
799      dy = dy * C - dx * S;
800      dx = tmp;
801
802      img_write_item(pimg_out, img_LINE, img_FLAG_SPLAY, NULL, x + dx, dy, z + dz);
803   }
804   p->splays = NULL;
805}
806
807static void
808do_stn(point *p, double X, const char *prefix, int dir, int labOnly,
809       double odx, double ody)
810{
811   leg *l, *lp;
812   double dX;
813   const stn *s;
814   int odir = dir;
815   int try_all;
816   int order = p->order;
817
818   for (s = p->stns; s; s = s->next) {
819      img_write_item(pimg_out, img_LABEL, s->flags, s->label, X, 0, p->p.z);
820   }
821
822   if (show_breaks && p->X != HUGE_VAL && p->X != X) {
823      /* Draw "surface" leg between broken stations. */
824      img_write_item(pimg_out, img_MOVE, 0, NULL, p->X, 0, p->p.z);
825      img_write_item(pimg_out, img_LINE, img_FLAG_SURFACE, NULL, X, 0, p->p.z);
826   }
827   p->X = X;
828   if (labOnly || p->fBroken) {
829      return;
830   }
831
832
833   if (order == 0) {
834      /* We've reached a dead end. */
835      do_splays(p, X, dir, odx, ody);
836      return;
837   }
838
839   /* It's better to follow legs along a survey, so make two passes and only
840    * follow legs in the same survey for the first pass.
841    */
842   for (try_all = 0; try_all != 2; ++try_all) {
843      lp = &headleg;
844      for (l = lp->next; l; lp = l, l = lp->next) {
845         dir = odir;
846         if (l->fDone) {
847            /* this case happens iff a recursive call causes the next leg to be
848             * removed, leaving our next pointing to a leg which has been dealt
849             * with... */
850            continue;
851         }
852         if (!try_all && l->prefix != prefix) {
853            continue;
854         }
855         int break_flag;
856         point *p2;
857         if (l->to == p) {
858            break_flag = BREAK_TO;
859            p2 = l->fr;
860         } else if (l->fr == p) {
861            break_flag = BREAK_FR;
862            p2 = l->to;
863         } else {
864            continue;
865         }
866         if (l->broken & break_flag) continue;
867         lp->next = l->next;
868         /* adjust direction of extension if necessary */
869         dir = adjust_direction(dir, p->dir);
870         dir = adjust_direction(dir, l->dir);
871
872         double dx = p2->p.x - p->p.x;
873         double dy = p2->p.y - p->p.y;
874         dX = hypot(dx, dy);
875         double X2 = X;
876         if (dir == ELEFT) {
877            X2 -= dX;
878         } else {
879            X2 += dX;
880         }
881
882         if (p->splays) {
883            do_splays(p, X, dir, odx + dx, ody + dy);
884         }
885
886         img_write_item(pimg_out, img_MOVE, 0, NULL, X, 0, p->p.z);
887         img_write_item(pimg_out, img_LINE, l->flags, l->prefix,
888                        X2, 0, p2->p.z);
889
890         /* We arrive at p2 via a leg, so that's one down right away. */
891         --p2->order;
892
893         l->fDone = 1;
894         /* l->broken doesn't have break_flag set as we checked that above. */
895         do_stn(p2, X2, l->prefix, dir, l->broken, dx, dy);
896         l = lp;
897         if (--order == 0) return;
898      }
899   }
900}
Note: See TracBrowser for help on using the repository browser.