// twhither.c // // by John D. de Boer #include "twist.h" extern TimeGlobs Time; extern StepGlobs Step; // Allocation void InitTravels(void) { } static void GrowTravels(Travels *T) { ArrDep *M; if (!T) return; T->num++; minimumsize(T->ad,T->num,sizeof(ArrDep)); lock(T->ad); M= (*(T->ad)) + T->num - 1; InitSpan(&(M->time)); M->city=NULL; unlock(T->ad);} Travels *NewTravels(const Place *C) { Travels *R; R=pointer(sizeof(Travels)); R->num=0; R->ad=(ArrDep **)handle(0); AddStop(R,C); return R; } void OldTravels(Travels *T) { if (!T) return; if (T->ad) release(T->ad); old(T); } // Adding to itineraries from ttt files void AddArrival(Travels *T, const Day *D) { ArrDep *M; if (!T || !D) return; GrowTravels(T); lock(T->ad); M= (*(T->ad)) + T->num - 1; M->time.start=*D; unlock(T->ad); } void AddStop(Travels *T, const Place *C) { ArrDep *M; if (!T) return; if (!C) { Warning("Itinerary place data missing"); return; } if (T->num<1 || (*(T->ad))[T->num - 1].city) GrowTravels(T); lock(T->ad); M= (*(T->ad)) + T->num - 1; M->city=(Place *)C; unlock(T->ad); } void AddDeparture(Travels *T, const Day *D) { ArrDep *M; if (!T || !D || T->num<1) return; lock(T->ad); M= (*(T->ad)) + T->num - 1; M->time.end=*D; unlock(T->ad); } // These functions fill in missing departure and arrival dates with approximate times, given // two known or assumed times. stime() allows access to N arrival/departure pairs as an // array of 2N times. static Day *stime(ArrDep *MP, int i) { MP+= i / 2; return (i & 1) ? &(MP->time.end) : &(MP->time.start); } static void GuessMissingTimes(ArrDep *M, int N) { int i,j,next,TwoN; Day *DI,*DJ,*DK; real Step,Unc; TwoN= 2 * N; for (i=0;i < TwoN - 2;i++) { DI=stime(M,i); DJ=stime(M,i + 1); if (DI->kind==timeJD && DJ->kind==timeInval) { next=-1; for (j= i + 2;jkind==timeJD) { next=j; break; } } if (next<0) continue; DK=stime(M,next); Step= (DK->jd - DI->jd) / (real)(next - i); Unc= (0.5 * (DI->unc + DK->unc)) + (0.4 * (DK->jd - DI->jd)); for (j= i + 1;jjd= DI->jd + ((j - i) * Step); DJ->unc=Unc; DJ->kind=timeJD; DJ->guess=1; } } } } // static void TruncateEarlyUnc(Day *A, const Day *B) { if (!A || !B) return; A->unc=between(A->jd - B->jd + B->unc,0,A->unc); } static void TruncateLateUnc(Day *A, const Day *B) { if (!A || !B) return; A->unc=between(B->jd - A->jd + B->unc,0,A->unc); } void VerifyTravels(Travels *T, const Span *Life, char *S) { int i,TwoN; ArrDep *M; Day *DI; bool InOrder; if (!T || !Life) return; if (T->num<1) { WarnOld(cat3(0,"Travels of ",S," have no waypoints")); return; } lock(T->ad); M=*(T->ad); DI=&(M[0 ].time.start); if (DI->kind!=timeJD) *DI=Life->start; if (DI->jdstart.jd) WarnOld(cat3(0,"Itinerary of ",S," begins too early")); DI=&(M[T->num - 1].time.end ); if (DI->kind!=timeJD) *DI=Life->end; if (DI->jd>Life->end.jd) WarnOld(cat3(0,"Itinerary of ",S," continues too late")); GuessMissingTimes(M,T->num); for (i=0;inum;i++) { Place *C; Span *Sp; C=M[i].city; Sp=&(M[i].time); if (!C) { WarnOld(cat5(2,"Waypoint #",fig(i + 1)," of itinerary of ",S," has invalid place")); continue; } if (Sp->start.kind==timeInval) { if (i>0) Sp->start=M[i - 1].time.end; WarnOld(cat5(0,"Stay of ",S," in ",C->name," has missing or invalid arrival time")); } if (Sp->end.kind==timeInval) { if (i + 1 < T->num) Sp->end=M[i + 1].time.start; WarnOld(cat5(0,"Stay of ",S," in ",C->name," has missing or invalid departure time")); } if (i == T->num - 1 && Sp->end.kind==timeJD) { if (Life->end.kind==timeJD && Sp->end.jdend.jd) WarnOld((cat4(0,"Itinerary of ",S," ends prematurely in/at ",C->name))); } PlaceReference(C,Sp); } for (i=0;i < T->num - 1;i++) { Day *D1,*D2; D1=&(M[i].time.start); D2=&(M[i + 1].time.start); TruncateEarlyUnc(D2,D1); } for (i= T->num - 2;i>=0;i--) { Day *D1,*D2; D1=&(M[i].time.end); D2=&(M[i + 1].time.end); TruncateLateUnc(D1,D2); } T->time.start=M[0].time.end; T->time.end=M[T->num - 1].time.start; InOrder=1; TwoN= T->num * 2; for (i=0;i < TwoN - 1;i++) if (!DatesInOrder(stime(M,i),stime(M,i + 1),0)) InOrder=0; if (!InOrder) DangerOld(cat3(0,"Itinerary of ",S," is not in order")); unlock(T->ad); } // Getting the current location of an itinerary Place *CurrentLocation(const Travels *T, const Day *D) { int i; ArrDep M; if (!T || !D || D->kind!=timeJD) return NULL; for (i=0;inum;i++) { M=(*(T->ad))[i]; if (DatesInOrder(D,&(M.time.end),0)) return M.city; } return NULL; } bool TravelMatch(const Travels *T, const char *S) { int i; ArrDep M; if (!T || !S) return 0; for (i=0;inum;i++) { M=(*(T->ad))[i]; if (PlaceMatch(M.city,S)) return 1; } return 0; } // Calculations for drawing travels static void DoFracCalc(long JD, const Day *D1, const Day *D2, real *F, int UNC) { long DUR; long J1,J2; if (!F) return; *F=0.0; if (!D1 || D1->kind==timeInval) return; if (!D2 || D2->kind==timeInval) return; J1= D1->jd + (UNC * D1->unc); if (JD<=J1) { *F=0.0; return; } J2= D2->jd + (UNC * D2->unc); if (JD>=J2) { *F=1.0; return; } DUR= J2 - J1; if (DUR<1) *F=0.5; else *F=fgtr(0.0,(JD - J1) / (real)DUR); } static void TravCalc(int NUM, ArrDep *M, Span *T, Progress *S, Progress *E, bool Unc) { int i,NMO; if (!M || !T || !S || !E) return; NMO= NUM - 1; S->leg=0; S->frac=0.0; E->leg=NMO; E->frac=0.0; if (DatesInStrictOrder(&(Step.intv.end),&(T->start),Unc)) { // if itinerary not started E->leg=0; S->c1=S->c2=E->c1=E->c2=M[0].city; return; } if (DatesInStrictOrder(&(T->end),&(Step.intv.start),Unc)) { // if itinerary finished S->leg=E->leg; S->c1=S->c2=E->c1=E->c2=M[NMO].city; return; } for (i=0;ifrac),Unc ? 1 : 0); S->leg=i; if (S->frac>=1.0) { S->leg++; S->frac=0.0; } } if (M[i + 1].there==travNotYet) { DoFracCalc(Step.intv.end.jd + 1,&(M[i].time.end),&(M[i + 1].time.start),&(E->frac),Unc ? -1 : 0); E->leg=i; if (S->frac>=1.0) { E->leg++; E->frac=0.0; } } } S->c1=M[S->leg].city; S->c2= (S->frac==0.0) ? S->c1 : M[S->leg + 1].city; E->c1=M[E->leg].city; E->c2= (E->frac==0.0) ? E->c1 : M[E->leg + 1].city; if (S->c1==S->c2) S->frac=0.0; if (E->c1==E->c2) E->frac=0.0; } void DoTravelCalcs(Travels *T) { if (!T) return; lock(T->ad); TravCalc(T->num,*(T->ad),&(T->time),&(T->st),&(T->en),0); TravCalc(T->num,*(T->ad),&(T->time),&(T->ust),&(T->uen),1); unlock(T->ad); }