2 * Survex network reduction - remove trailing traverses and concatenate
3 * traverses between junctions
4 * Copyright (C) 1991-2004,2005,2006,2010,2011,2012,2013,2014,2015 Olly Betts
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.
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.
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
21 /* #define BLUNDER_DETECTION */
24 #define DEBUG_INVALID 1
26 #define DUMP_NETWORK 1
39 #include "img_hosted.h"
46 #define sqrdd(X) (sqrd((X)[0]) + sqrd((X)[1]) + sqrd((X)[2]))
48 typedef struct Stack
{
49 struct Link
*join1
, *join2
;
53 typedef struct StackTr
{
58 /* string used between things in traverse printouts eg \1 - \2 - \3 -...*/
59 static const char *szLink
= " - ";
60 static const char *szLinkEq
= " = "; /* use this one for equates */
63 #define fprint_prefix(FH, NAME) BLK((fprint_prefix)((FH), (NAME));\
64 fprintf((FH), " [%p]", (void*)(NAME)); )
67 static stack
*ptr
; /* Ptr to TRaverse linked list for in-between travs */
68 static stackTrail
*ptrTrail
; /* Ptr to TRaverse linked list for trail travs*/
70 static void remove_trailing_travs(void);
71 static void remove_travs(void);
72 static void replace_travs(void);
73 static void replace_trailing_travs(void);
74 static void write_passage_models(void);
76 static void concatenate_trav(node
*stn
, int i
);
78 static void err_stat(int cLegsTrav
, double lenTrav
,
79 double eTot
, double eTotTheo
,
80 double hTot
, double hTotTheo
,
81 double vTot
, double vTotTheo
);
84 solve_network(void /*node *stnlist*/)
86 static int first_solve
= 1;
89 /* We can't average across solving to fix positions. */
92 if (stnlist
== NULL
) {
93 if (first_solve
) fatalerror(/*No survey data*/43);
94 /* We've had a *solve followed by another *solve (or the implicit
95 * *solve at the end of the data. Don't moan about that. */
102 if (first_solve
&& !pcs
->proj_str
&& !proj_str_out
) {
103 /* If we haven't already solved to find some station positions, and
104 * there's no specified coordinate system, then check if there are any
105 * fixed points, and if there aren't, invent one at (0,0,0).
107 * We do this first so the solving part is just like the standard case -
108 * this avoid problems, such as sub-nodes of the invented fix having been
109 * removed. It also means we can fix the "first" station, which makes
110 * more sense to the user. */
111 FOR_EACH_STN(stn
, stnlist
)
112 if (fixed(stn
)) break;
115 /* If we've had a *solve and all the new survey since then is hanging,
116 * we don't want to invent a fixed point. We want to complain but
117 * the easiest way to is just to continue processing and let
118 * articulate() catch this condition as it will any hanging survey
120 node
*stnFirst
= NULL
;
122 /* New stations are pushed onto the head of the list, so the
123 * first station added is the last in the list. */
124 FOR_EACH_STN(stn
, stnlist
) {
125 /* Prefer a station with legs attached when choosing one to fix
126 * so that if there's a hanging station on a nosurvey leg we pick
127 * the main clump of survey data. */
128 if (stnFirst
&& !stnFirst
->leg
[0]) continue;
132 /* If we've got nosurvey legs, then the station we find to fix could
133 * have no real legs attached. */
134 SVX_ASSERT2(nosurveyhead
|| stnFirst
->leg
[0],
135 "no fixed stns, but we've got a zero node!");
136 SVX_ASSERT2(stnFirst
, "no stations left in net!");
138 printf(msg(/*Survey has no fixed points. Therefore I’ve fixed %s at (0,0,0)*/72),
139 sprint_prefix(stn
->name
));
141 POS(stn
,0) = (real
)0.0;
142 POS(stn
,1) = (real
)0.0;
143 POS(stn
,2) = (real
)0.0;
150 remove_trailing_travs();
151 validate(); dump_network();
153 validate(); dump_network();
155 validate(); dump_network();
157 validate(); dump_network();
159 validate(); dump_network();
161 validate(); dump_network();
162 replace_trailing_travs();
163 validate(); dump_network();
165 /* Now write out any passage models. */
166 write_passage_models();
170 remove_trailing_travs(void)
173 /* TRANSLATORS: In French, Eric chose to use the terminology used by
174 * toporobot: "sequence" for the English "traverse", which makes sense
175 * (although toporobot actually uses this term to mean something more
176 * specific). Feel free to follow this lead if you can't think of a better
177 * term - these messages mostly indicate how processing is progressing.
179 * A trailing traverse is a dead end back to a junction. */
180 out_current_action(msg(/*Removing trailing traverses*/125));
181 FOR_EACH_STN(stn
, stnlist
) {
182 if (!fixed(stn
) && one_node(stn
)) {
189 printf("Removed trailing trav ");
194 print_prefix(stn2
->name
); printf("<%p>",stn2
); fputs(szLink
, stdout
);
196 remove_stn_from_list(&stnlist
, stn2
);
198 j
= reverse_leg_dirn(leg
);
200 i
= j
^ 1; /* flip direction for other leg of 2 node */
201 /* stop if fixed or 3 or 1 node */
202 } while (two_node(stn2
) && !fixed(stn2
));
204 /* put traverse on stack */
205 trav
= osnew(stackTrail
);
206 trav
->join1
= stn2
->leg
[j
];
207 trav
->next
= ptrTrail
;
210 /* We want to keep all 2-nodes using legs 0 and 1 and all one nodes
211 * using leg 0 so we may need to swap leg j with leg 2 (for a 3 node)
212 * or leg 1 (for a fixed 2 node) */
213 if ((j
== 0 && !one_node(stn2
)) || (j
== 1 && three_node(stn2
))) {
214 /* i is the direction to swap with */
215 i
= (three_node(stn2
)) ? 2 : 1;
216 /* change the other direction of leg i to use leg j */
217 reverse_leg(stn2
->leg
[i
])->l
.reverse
+= j
- i
;
218 stn2
->leg
[j
] = stn2
->leg
[i
];
224 print_prefix(stn2
->name
); printf("<%p>",stn2
); putnl();
234 /* TRANSLATORS: In French, Eric chose to use the terminology used by
235 * toporobot: "sequence" for the English "traverse", which makes sense
236 * (although toporobot actually uses this term to mean something more
237 * specific). Feel free to follow this lead if you can't think of a better
238 * term - these messages mostly indicate how processing is progressing. */
239 out_current_action(msg(/*Concatenating traverses*/126));
240 FOR_EACH_STN(stn
, stnlist
) {
241 if (fixed(stn
) || three_node(stn
)) {
243 for (d
= 0; d
<= 2; d
++) {
244 linkfor
*leg
= stn
->leg
[d
];
245 if (leg
&& !(leg
->l
.reverse
& FLAG_REPLACEMENTLEG
))
246 concatenate_trav(stn
, d
);
253 concatenate_trav(node
*stn
, int i
)
258 linkfor
*newleg
, *newleg2
;
260 stn2
= stn
->leg
[i
]->l
.to
;
261 /* Reject single legs as they may be already concatenated traverses */
262 if (fixed(stn2
) || !two_node(stn2
)) return;
265 newleg2
= (linkfor
*)osnew(linkrev
);
268 printf("Concatenating trav "); print_prefix(stn
->name
); printf("<%p>",stn
);
272 newleg2
->l
.reverse
= i
| FLAG_REPLACEMENTLEG
;
273 trav
->join1
= stn
->leg
[i
];
275 j
= reverse_leg_dirn(stn
->leg
[i
]);
276 SVX_ASSERT(j
== 0 || j
== 1);
278 newleg
= copy_link(stn
->leg
[i
]);
284 fputs(szLink
, stdout
); print_prefix(stn
->name
); printf("<%p>",stn
);
287 /* stop if fixed or 3 or 1 node */
288 if (fixed(stn
) || !two_node(stn
)) break;
290 remove_stn_from_list(&stnlist
, stn
);
292 i
= j
^ 1; /* flip direction for other leg of 2 node */
294 stn2
= stn
->leg
[i
]->l
.to
;
295 j
= reverse_leg_dirn(stn
->leg
[i
]);
297 addto_link(newleg
, stn
->leg
[i
]);
300 trav
->join2
= stn
->leg
[j
];
305 newleg
->l
.reverse
= j
| FLAG_DATAHERE
| FLAG_REPLACEMENTLEG
;
307 newleg2
->l
.to
->leg
[reverse_leg_dirn(newleg2
)] = newleg
;
308 /* i.e. stn->leg[i] = newleg; with original stn and i */
310 stn
->leg
[j
] = newleg2
;
314 print_var(&(newleg
->v
));
315 printf("\nStacked ");
316 print_prefix(newleg2
->l
.to
->name
);
317 printf(",%d-", reverse_leg_dirn(newleg2
));
318 print_prefix(stn
->name
);
323 #ifdef BLUNDER_DETECTION
324 /* expected_error is actually squared... */
325 /* only called if fhErrStat != NULL */
327 do_gross(delta e
, delta v
, node
*stn1
, node
*stn2
, double expected_error
)
329 double hsqrd
, rsqrd
, s
, cx
, cy
, cz
;
333 prefix
*name1
= stn1
->name
, *name2
= stn2
->name
;
336 printf( "e = ( %.2f, %.2f, %.2f )", e
[0], e
[1], e
[2] );
337 printf( " v = ( %.2f, %.2f, %.2f )\n", v
[0], v
[1], v
[2] );
339 hsqrd
= sqrd(v
[0]) + sqrd(v
[1]);
340 rsqrd
= hsqrd
+ sqrd(v
[2]);
341 if (rsqrd
== 0.0) return;
347 s
= (e
[0] * v
[0] + e
[1] * v
[1] + e
[2] * v
[2]) / rsqrd
;
349 for (i
= 2; i
>= 0; i
--) tot
+= sqrd(e
[i
] - v
[i
] * s
);
351 if (tot
<= expected_error
) {
353 fprint_prefix(fhErrStat
, name1
);
354 fputs("->", fhErrStat
);
355 fprint_prefix(fhErrStat
, name2
);
357 fprintf(fhErrStat
, " L: %.2f", sqrt(tot
));
358 /* checked - works */
359 fprintf(fhErrStat
, " (%.2fm -> %.2fm)", sqrt(sqrdd(v
)), sqrt(sqrdd(v
)) * (1 - s
));
363 s
= sqrd(cx
) + sqrd(cy
);
366 SVX_ASSERT(s
>= 0.0);
369 tot
= sqrd(cx
* s
) + sqrd(cy
* s
) + sqrd(e
[2]);
370 if (tot
<= expected_error
) {
371 double newval
, oldval
;
373 fprint_prefix(fhErrStat
, name1
);
374 fputs("->", fhErrStat
);
375 fprint_prefix(fhErrStat
, name2
);
377 fprintf(fhErrStat
, " B: %.2f", sqrt(tot
));
378 /* checked - works */
379 newval
= deg(atan2(cx
, cy
));
380 if (newval
< 0) newval
+= 360;
381 oldval
= deg(atan2(v
[0], v
[1]));
382 if (oldval
< 0) oldval
+= 360;
383 fprintf(fhErrStat
, " (%.2fdeg -> %.2fdeg)", oldval
, newval
);
390 s
= (e
[0] * v
[1] - e
[1] * v
[0]) / hsqrd
;
393 s
= sqrd(nx
) + sqrd(ny
) + sqrd(cz
);
398 tot
= sqrd(cx
- s
* nx
) + sqrd(cy
- s
* ny
) + sqrd(cz
- s
* cz
);
399 if (tot
<= expected_error
) {
401 fprint_prefix(fhErrStat
, name1
);
402 fputs("->", fhErrStat
);
403 fprint_prefix(fhErrStat
, name2
);
405 fprintf(fhErrStat
, " G: %.2f", sqrt(tot
));
406 /* checked - works */
407 fprintf(fhErrStat
, " (%.2fdeg -> %.2fdeg)",
408 deg(atan2(v
[2], sqrt(v
[0] * v
[0] + v
[1] * v
[1]))),
409 deg(atan2(cz
, sqrt(nx
* nx
+ ny
* ny
))));
414 if (output
) fputnl(fhErrStat
);
422 node
*stn1
, *stn2
, *stn3
;
424 double eTot
= 0, lenTrav
= 0, lenTot
;
426 double vTot
= 0, vTotTheo
= 0, hTot
= 0, hTotTheo
= 0;
428 bool fEquate
; /* used to indicate equates in output */
432 /* TRANSLATORS: In French, Eric chose to use the terminology used by
433 * toporobot: "sequence" for the English "traverse", which makes sense
434 * (although toporobot actually uses this term to mean something more
435 * specific). Feel free to follow this lead if you can't think of a better
436 * term - these messages mostly indicate how processing is progressing. */
437 out_current_action(msg(/*Calculating traverses*/127));
439 if (!fhErrStat
&& !fSuppress
)
440 fhErrStat
= safe_fopen_with_ext(fnm_output_base
, EXT_SVX_ERRS
, "w");
443 char *fnm
= add_ext(fnm_output_base
, EXT_SVX_3D
);
444 filename_register_output(fnm
);
445 pimg
= img_open_write_cs(fnm
, survey_title
, proj_str_out
, 0);
446 if (!pimg
) fatalerror(img_error(), fnm
);
450 /* First do all the one leg traverses */
451 FOR_EACH_STN(stn1
, stnlist
) {
453 printf("One leg traverses from ");
454 print_prefix(stn1
->name
);
455 printf(" [%p]\n", stn1
);
457 for (i
= 0; i
<= 2; i
++) {
458 linkfor
*leg
= stn1
->leg
[i
];
459 if (leg
&& data_here(leg
) &&
460 !(leg
->l
.reverse
& (FLAG_REPLACEMENTLEG
| FLAG_FAKE
))) {
461 SVX_ASSERT(fixed(stn1
));
462 SVX_ASSERT(!fZeros(&leg
->v
));
465 if (TSTBIT(leg
->l
.flags
, FLAGS_SURFACE
)) {
466 stn1
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
467 stn2
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
469 stn1
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
470 stn2
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
472 img_write_item(pimg
, img_MOVE
, 0, NULL
,
473 POS(stn1
, 0), POS(stn1
, 1), POS(stn1
, 2));
475 pimg
->days1
= leg
->meta
->days1
;
476 pimg
->days2
= leg
->meta
->days2
;
478 pimg
->days1
= pimg
->days2
= -1;
480 pimg
->style
= (leg
->l
.flags
>> FLAGS_STYLE_BIT0
) & 0x07;
481 img_write_item(pimg
, img_LINE
, leg
->l
.flags
& FLAGS_MASK
,
482 sprint_prefix(stn1
->name
->up
),
483 POS(stn2
, 0), POS(stn2
, 1), POS(stn2
, 2));
484 if (!(leg
->l
.reverse
& FLAG_ARTICULATION
)) {
485 #ifdef BLUNDER_DETECTION
490 fprint_prefix(fhErrStat
, stn1
->name
);
491 fputs(szLink
, fhErrStat
);
492 fprint_prefix(fhErrStat
, stn2
->name
);
495 subdd(&e
, &POSD(stn2
), &POSD(stn1
));
496 subdd(&e
, &e
, &leg
->d
);
499 hTot
= sqrd(e
[0]) + sqrd(e
[1]);
501 #ifndef NO_COVARIANCES
502 /* FIXME: what about covariances? */
503 hTotTheo
= leg
->v
[0] + leg
->v
[1];
504 vTotTheo
= leg
->v
[2];
505 eTotTheo
= hTotTheo
+ vTotTheo
;
507 hTotTheo
= leg
->v
[0] + leg
->v
[1];
508 vTotTheo
= leg
->v
[2];
509 eTotTheo
= hTotTheo
+ vTotTheo
;
511 #ifdef BLUNDER_DETECTION
512 memcpy(&err
, &e
, sizeof(delta
));
513 do_blunder
= (eTot
> eTotTheo
);
514 fputs("\ntraverse ", fhErrStat
);
515 fprint_prefix(fhErrStat
, stn1
->name
);
516 fputs("->", fhErrStat
);
517 fprint_prefix(fhErrStat
, stn2
->name
);
518 fprintf(fhErrStat
, " e=(%.2f, %.2f, %.2f) mag=%.2f %s\n",
519 e
[0], e
[1], e
[2], sqrt(eTot
),
520 (do_blunder
? "suspect:" : "OK"));
522 do_gross(err
, leg
->d
, stn1
, stn2
, eTotTheo
);
524 err_stat(1, sqrt(sqrdd(leg
->d
)), eTot
, eTotTheo
,
525 hTot
, hTotTheo
, vTot
, vTotTheo
);
532 while (ptr
!= NULL
) {
533 /* work out where traverse should be reconnected */
534 linkfor
*leg
= ptr
->join1
;
535 leg
= reverse_leg(leg
);
537 i
= reverse_leg_dirn(leg
);
540 leg
= reverse_leg(leg
);
542 j
= reverse_leg_dirn(leg
);
546 print_prefix(stn1
->name
);
547 printf("<%p>[%d]%s...%s", stn1
, i
, szLink
, szLink
);
548 print_prefix(stn2
->name
);
549 printf("<%p>[%d]\n", stn2
, j
);
552 SVX_ASSERT(fixed(stn1
));
553 SVX_ASSERT(fixed(stn2
));
555 /* calculate scaling factors for error distribution */
558 SVX_ASSERT(data_here(stn1
->leg
[i
]));
559 if (fZeros(&stn1
->leg
[i
]->v
)) {
560 sc
[0] = sc
[1] = sc
[2] = 0.0;
562 subdd(&e
, &POSD(stn2
), &POSD(stn1
));
563 subdd(&e
, &e
, &stn1
->leg
[i
]->d
);
565 hTot
= sqrd(e
[0]) + sqrd(e
[1]);
567 divds(&sc
, &e
, &stn1
->leg
[i
]->v
);
569 #ifndef NO_COVARIANCES
570 /* FIXME: what about covariances? */
571 hTotTheo
= stn1
->leg
[i
]->v
[0] + stn1
->leg
[i
]->v
[1];
572 vTotTheo
= stn1
->leg
[i
]->v
[2];
574 hTotTheo
= stn1
->leg
[i
]->v
[0] + stn1
->leg
[i
]->v
[1];
575 vTotTheo
= stn1
->leg
[i
]->v
[2];
577 eTotTheo
= hTotTheo
+ vTotTheo
;
580 img_write_item(pimg
, img_MOVE
, 0, NULL
,
581 POS(stn1
, 0), POS(stn1
, 1), POS(stn1
, 2));
583 fArtic
= stn1
->leg
[i
]->l
.reverse
& FLAG_ARTICULATION
;
584 osfree(stn1
->leg
[i
]);
585 stn1
->leg
[i
] = ptr
->join1
; /* put old link back in */
587 osfree(stn2
->leg
[j
]);
588 stn2
->leg
[j
] = ptr
->join2
; /* and the other end */
590 #ifdef BLUNDER_DETECTION
593 memcpy(&err
, &e
, sizeof(delta
));
594 do_blunder
= (eTot
> eTotTheo
);
595 if (fhErrStat
&& !fArtic
) {
596 fputs("\ntraverse ", fhErrStat
);
597 fprint_prefix(fhErrStat
, stn1
->name
);
598 fputs("->", fhErrStat
);
599 fprint_prefix(fhErrStat
, stn2
->name
);
600 fprintf(fhErrStat
, " e=(%.2f, %.2f, %.2f) mag=%.2f %s\n",
601 e
[0], e
[1], e
[2], sqrt(eTot
),
602 (do_blunder
? "suspect:" : "OK"));
610 /* get next node in traverse
611 * should have stn3->leg[k]->l.to == stn1 */
612 stn3
= stn1
->leg
[i
]->l
.to
;
613 k
= reverse_leg_dirn(stn1
->leg
[i
]);
614 SVX_ASSERT2(stn3
->leg
[k
]->l
.to
== stn1
,
615 "reverse leg doesn't reciprocate");
617 reached_end
= (stn3
== stn2
&& k
== j
);
619 if (data_here(stn1
->leg
[i
])) {
620 leg_pfx
= stn1
->name
->up
;
622 #ifdef BLUNDER_DETECTION
623 if (do_blunder
&& fhErrStat
)
624 do_gross(err
, leg
->d
, stn1
, stn3
, eTotTheo
);
627 adddd(&POSD(stn3
), &POSD(stn1
), &leg
->d
);
629 leg_pfx
= stn3
->name
->up
;
631 #ifdef BLUNDER_DETECTION
632 if (do_blunder
&& fhErrStat
)
633 do_gross(err
, leg
->d
, stn1
, stn3
, eTotTheo
);
636 subdd(&POSD(stn3
), &POSD(stn1
), &leg
->d
);
639 lenTot
= sqrdd(leg
->d
);
641 if (!fZeros(&leg
->v
)) fEquate
= fFalse
;
643 add_stn_to_list(&stnlist
, stn3
);
645 mulsd(&e
, &leg
->v
, &sc
);
646 adddd(&POSD(stn3
), &POSD(stn3
), &e
);
651 if (!(leg
->l
.reverse
& (FLAG_REPLACEMENTLEG
| FLAG_FAKE
))) {
652 if (TSTBIT(leg
->l
.flags
, FLAGS_SURFACE
)) {
653 stn1
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
654 stn3
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
656 stn1
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
657 stn3
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
660 SVX_ASSERT(!fEquate
);
661 SVX_ASSERT(!fZeros(&leg
->v
));
663 pimg
->days1
= leg
->meta
->days1
;
664 pimg
->days2
= leg
->meta
->days2
;
666 pimg
->days1
= pimg
->days2
= -1;
668 pimg
->style
= (leg
->l
.flags
>> FLAGS_STYLE_BIT0
) & 0x07;
669 img_write_item(pimg
, img_LINE
, leg
->l
.flags
& FLAGS_MASK
,
670 sprint_prefix(leg_pfx
),
671 POS(stn3
, 0), POS(stn3
, 1), POS(stn3
, 2));
674 /* FIXME: equate at the start of a traverse treated specially
675 * - what about equates at end? */
676 if (stn1
->name
!= stn3
->name
&& !(fEquate
&& cLegsTrav
== 0)) {
677 /* (node not part of same stn) &&
678 * (not equate at start of traverse) */
679 #ifndef BLUNDER_DETECTION
680 if (fhErrStat
&& !fArtic
) {
681 if (!stn1
->name
->ident
) {
682 /* FIXME: not ideal */
683 fputs("<fixed point>", fhErrStat
);
685 fprint_prefix(fhErrStat
, stn1
->name
);
687 fputs(fEquate
? szLinkEq
: szLink
, fhErrStat
);
689 if (!stn3
->name
->ident
) {
690 /* FIXME: not ideal */
691 fputs("<fixed point>", fhErrStat
);
693 fprint_prefix(fhErrStat
, stn3
->name
);
700 lenTrav
+= sqrt(lenTot
);
703 #if SHOW_INTERNAL_LEGS
704 if (fhErrStat
&& !fArtic
) fprintf(fhErrStat
, "+");
708 fprintf(stderr
, "lenTot = %8.4f ", lenTot
);
709 fprint_prefix(stderr
, stn1
->name
);
710 fprintf(stderr
, " -> ");
711 fprint_prefix(stderr
, stn3
->name
);
713 BUG("during calculation of closure errors");
716 if (reached_end
) break;
718 i
= k
^ 1; /* flip direction for other leg of 2 node */
723 if (cLegsTrav
&& !fArtic
&& fhErrStat
)
724 err_stat(cLegsTrav
, lenTrav
, eTot
, eTotTheo
,
725 hTot
, hTotTheo
, vTot
, vTotTheo
);
732 /* Leave fhErrStat open in case we're asked to close loops again... */
736 err_stat(int cLegsTrav
, double lenTrav
,
737 double eTot
, double eTotTheo
,
738 double hTot
, double hTotTheo
,
739 double vTot
, double vTotTheo
)
741 double E
= sqrt(eTot
/ eTotTheo
);
742 double H
= sqrt(hTot
/ hTotTheo
);
743 double V
= sqrt(vTot
/ vTotTheo
);
745 double sqrt_eTot
= sqrt(eTot
);
747 fprintf(fhErrStat
, msg(/*Original length %6.2fm (%3d legs), moved %6.2fm (%5.2fm/leg). */145),
748 lenTrav
, cLegsTrav
, sqrt_eTot
, sqrt_eTot
/ cLegsTrav
);
750 fprintf(fhErrStat
, msg(/*Error %6.2f%%*/146), 100 * sqrt_eTot
/ lenTrav
);
752 /* TRANSLATORS: Here N/A means "Not Applicable" -- it means the
753 * traverse has zero length, so error per metre is meaningless.
755 * There should be 4 spaces between "Error" and "N/A" so that it lines
756 * up with the numbers in the message above. */
757 fputs(msg(/*Error N/A*/147), fhErrStat
);
760 fprintf(fhErrStat
, "%f\n", E
);
761 fprintf(fhErrStat
, "H: %f V: %f\n", H
, V
);
764 img_write_errors(pimg
, cLegsTrav
, lenTrav
, E
, H
, V
);
768 replace_trailing_travs(void)
775 /* TRANSLATORS: In French, Eric chose to use the terminology used by
776 * toporobot: "sequence" for the English "traverse", which makes sense
777 * (although toporobot actually uses this term to mean something more
778 * specific). Feel free to follow this lead if you can't think of a better
779 * term - these messages mostly indicate how processing is progressing.
781 * A trailing traverse is a dead end back to a junction. */
782 out_current_action(msg(/*Calculating trailing traverses*/128));
784 while (ptrTrail
!= NULL
) {
785 leg
= ptrTrail
->join1
;
786 leg
= reverse_leg(leg
);
788 i
= reverse_leg_dirn(leg
);
790 printf(" Trailing trav ");
791 print_prefix(stn1
->name
);
792 printf("<%p>", stn1
);
793 printf("%s...\n", szLink
);
794 printf(" attachment stn is at (%f, %f, %f)\n",
795 POS(stn1
, 0), POS(stn1
, 1), POS(stn1
, 2));
797 /* We may have swapped the links round when we removed the leg. If
798 * we did then stn1->leg[i] will be in use. The link we swapped
799 * with is the first free leg */
801 /* j is the direction to swap with */
802 int j
= (stn1
->leg
[1]) ? 2 : 1;
803 /* change the other direction of leg i to use leg j */
804 reverse_leg(stn1
->leg
[i
])->l
.reverse
+= j
- i
;
805 stn1
->leg
[j
] = stn1
->leg
[i
];
807 stn1
->leg
[i
] = ptrTrail
->join1
;
808 SVX_ASSERT(fixed(stn1
));
809 img_write_item(pimg
, img_MOVE
, 0, NULL
,
810 POS(stn1
, 0), POS(stn1
, 1), POS(stn1
, 2));
818 j
= reverse_leg_dirn(leg
);
819 if (data_here(leg
)) {
820 leg_pfx
= stn1
->name
->up
;
821 adddd(&POSD(stn2
), &POSD(stn1
), &leg
->d
);
823 printf("Adding leg (%f, %f, %f)\n", leg
->d
[0], leg
->d
[1], leg
->d
[2]);
826 leg_pfx
= stn2
->name
->up
;
828 subdd(&POSD(stn2
), &POSD(stn1
), &leg
->d
);
830 printf("Subtracting reverse leg (%f, %f, %f)\n", leg
->d
[0], leg
->d
[1], leg
->d
[2]);
835 add_stn_to_list(&stnlist
, stn2
);
836 if (!(leg
->l
.reverse
& (FLAG_REPLACEMENTLEG
| FLAG_FAKE
))) {
837 if (TSTBIT(leg
->l
.flags
, FLAGS_SURFACE
)) {
838 stn1
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
839 stn2
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
841 stn1
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
842 stn2
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
845 if (!(leg
->l
.reverse
& (FLAG_REPLACEMENTLEG
| FLAG_FAKE
))) {
846 SVX_ASSERT(!fZeros(&leg
->v
));
848 pimg
->days1
= leg
->meta
->days1
;
849 pimg
->days2
= leg
->meta
->days2
;
851 pimg
->days1
= pimg
->days2
= -1;
853 pimg
->style
= (leg
->l
.flags
>> FLAGS_STYLE_BIT0
) & 0x07;
854 img_write_item(pimg
, img_LINE
, leg
->l
.flags
& FLAGS_MASK
,
855 sprint_prefix(leg_pfx
),
856 POS(stn2
, 0), POS(stn2
, 1), POS(stn2
, 2));
859 /* stop if not 2 node */
860 if (!two_node(stn2
)) break;
863 i
= j
^ 1; /* flip direction for other leg of 2 node */
867 ptrTrail
= ptrTrail
->next
;
871 /* write out connections with no survey data */
872 while (nosurveyhead
) {
873 nosurveylink
*p
= nosurveyhead
;
874 SVX_ASSERT(fixed(p
->fr
));
875 SVX_ASSERT(fixed(p
->to
));
876 if (TSTBIT(p
->flags
, FLAGS_SURFACE
)) {
877 p
->fr
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
878 p
->to
->name
->sflags
|= BIT(SFLAGS_SURFACE
);
880 p
->fr
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
881 p
->to
->name
->sflags
|= BIT(SFLAGS_UNDERGROUND
);
883 img_write_item(pimg
, img_MOVE
, 0, NULL
,
884 POS(p
->fr
, 0), POS(p
->fr
, 1), POS(p
->fr
, 2));
886 pimg
->days1
= p
->meta
->days1
;
887 pimg
->days2
= p
->meta
->days2
;
889 pimg
->days1
= pimg
->days2
= -1;
891 pimg
->style
= img_STYLE_NOSURVEY
;
892 img_write_item(pimg
, img_LINE
, (p
->flags
& FLAGS_MASK
),
893 sprint_prefix(p
->fr
->name
->up
),
894 POS(p
->to
, 0), POS(p
->to
, 1), POS(p
->to
, 2));
895 nosurveyhead
= p
->next
;
899 /* write stations to .3d file and free legs and stations */
900 FOR_EACH_STN(stn1
, stnlist
) {
902 SVX_ASSERT(fixed(stn1
));
903 if (stn1
->name
->stn
== stn1
) {
904 int sf
= stn1
->name
->sflags
;
905 /* take care of unused fixed points */
906 if (!TSTBIT(sf
, SFLAGS_SOLVED
)) {
907 const char * label
= NULL
;
908 if (TSTBIT(sf
, SFLAGS_ANON
)) {
910 } else if (stn1
->name
->ident
) {
911 label
= sprint_prefix(stn1
->name
);
914 /* Set flag to stop station being rewritten after *solve. */
915 stn1
->name
->sflags
= sf
| BIT(SFLAGS_SOLVED
);
917 if (stn1
->name
->max_export
) sf
|= BIT(SFLAGS_EXPORTED
);
918 img_write_item(pimg
, img_LABEL
, sf
, label
,
919 POS(stn1
, 0), POS(stn1
, 1), POS(stn1
, 2));
923 /* update coords of bounding box, ignoring the base positions
924 * of points fixed with error estimates and only counting stations
925 * in underground surveys.
927 * NB We don't set SFLAGS_UNDERGROUND for the anchor station for
928 * a point fixed with error estimates, so this test will exclude
929 * those too, which is what we want.
931 if (TSTBIT(stn1
->name
->sflags
, SFLAGS_UNDERGROUND
)) {
932 for (d
= 0; d
< 3; d
++) {
933 if (POS(stn1
, d
) < min
[d
]) {
934 min
[d
] = POS(stn1
, d
);
935 pfxLo
[d
] = stn1
->name
;
937 if (POS(stn1
, d
) > max
[d
]) {
938 max
[d
] = POS(stn1
, d
);
939 pfxHi
[d
] = stn1
->name
;
943 /* Range without anonymous stations at offset 3. */
944 if (!TSTBIT(stn1
->name
->sflags
, SFLAGS_ANON
)) {
945 for (d
= 0; d
< 3; d
++) {
946 if (POS(stn1
, d
) < min
[d
+ 3]) {
947 min
[d
+ 3] = POS(stn1
, d
);
948 pfxLo
[d
+ 3] = stn1
->name
;
950 if (POS(stn1
, d
) > max
[d
+ 3]) {
951 max
[d
+ 3] = POS(stn1
, d
);
952 pfxHi
[d
+ 3] = stn1
->name
;
958 d
= stn1
->name
->shape
;
959 if (d
<= 1 && !TSTBIT(stn1
->name
->sflags
, SFLAGS_USED
)) {
960 bool unused_fixed_point
= fFalse
;
962 /* Unused fixed point without error estimates */
963 unused_fixed_point
= fTrue
;
964 } else if (stn1
->leg
[0]) {
965 prefix
*pfx
= stn1
->leg
[0]->l
.to
->name
;
966 if (!pfx
->ident
&& !TSTBIT(pfx
->sflags
, SFLAGS_ANON
)) {
967 /* Unused fixed point with error estimates */
968 unused_fixed_point
= fTrue
;
971 if (unused_fixed_point
) {
972 /* TRANSLATORS: fixed survey station that is not part of any survey
974 warning_in_file(stn1
->name
->filename
, stn1
->name
->line
,
975 /*Unused fixed point “%s”*/73, sprint_prefix(stn1
->name
));
979 /* For stations fixed with error estimates, we need to ignore the leg to
980 * the "real" fixed point in the node stats.
982 if (stn1
->leg
[0] && !stn1
->leg
[0]->l
.to
->name
->ident
&&
983 !TSTBIT(stn1
->leg
[0]->l
.to
->name
->sflags
, SFLAGS_ANON
))
986 for (i
= 0; i
<= 2; i
++) {
988 /* only want to think about forwards legs */
989 if (leg
&& data_here(leg
)) {
994 iB
= reverse_leg_dirn(leg
);
995 legRev
= stnB
->leg
[iB
];
996 SVX_ASSERT2(legRev
->l
.to
== stn1
, "leg doesn't reciprocate");
997 SVX_ASSERT(fixed(stn1
));
999 (BIT(FLAGS_DUPLICATE
)|BIT(FLAGS_SPLAY
)|
1000 BIT(FLAGS_SURFACE
)))) {
1001 /* check not an equating leg, or one inside an sdfix point */
1002 if (!(leg
->l
.reverse
& (FLAG_REPLACEMENTLEG
| FLAG_FAKE
))) {
1003 totadj
+= sqrt(sqrd(POS(stnB
, 0) - POS(stn1
, 0)) +
1004 sqrd(POS(stnB
, 1) - POS(stn1
, 1)) +
1005 sqrd(POS(stnB
, 2) - POS(stn1
, 2)));
1006 total
+= sqrt(sqrdd(leg
->d
));
1007 totplan
+= hypot(leg
->d
[0], leg
->d
[1]);
1008 totvert
+= fabs(leg
->d
[2]);
1013 stn1
->leg
[i
] = stnB
->leg
[iB
] = NULL
;
1018 /* The station position is attached to the name, so we leave the names and
1019 * positions in place - they can then be picked up if we have a *solve
1020 * followed by more data */
1021 for (stn1
= stnlist
; stn1
; stn1
= stn2
) {
1023 stn1
->name
->stn
= NULL
;
1030 write_passage_models(void)
1032 lrudlist
* psg
= model
;
1035 lrud
* xsect
= psg
->tube
;
1046 pimg
->days1
= xsect
->meta
->days1
;
1047 pimg
->days2
= xsect
->meta
->days2
;
1049 pimg
->days1
= pimg
->days2
= -1;
1053 name
= sprint_prefix(pfx
);
1055 xsect
= xsect
->next
;
1059 /* TRANSLATORS: e.g. the user specifies a passage cross-section at
1060 * station "entrance.27", but there is no station "entrance.27" in
1061 * the centre-line. */
1062 error_in_file(pfx
->filename
, pfx
->line
,
1063 /*Cross section specified at non-existent station “%s”*/83,
1066 if (xsect
== NULL
) xflags
= img_XFLAG_END
;
1067 img_write_item(pimg
, img_XSECT
, xflags
, name
, 0, 0, 0);