source: git/src/extend.c @ 76cf7f1

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-data
Last change on this file since 76cf7f1 was e67cb60, checked in by Olly Betts <olly@…>, 7 years ago

Change rotation of splays in extend

Rotate splays based on bearing between stations either side of the
current one. This nicely handles the situation at the top or
bottom of a pitch, and should tend to pick an angle closer to the
passage orientation.

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