// drawplace.c // // This file determines what scale of geographic names to show at a given time and magnification, and // draws the appropriate names on the globe. It also draws spheres of influence and itineraries. // // by John D. de Boer #include "twist.h" #include "twindow.h" extern TimeGlobs Time; extern GlobalOpts Opt; extern StepGlobs Step; extern void *Sel; extern int PLACES,ORDER,SPHERES; extern Place ***Places; extern void ***Order; extern Sphere ***Spheres; extern projection *Proj; extern const real EarthRad; // Drawing spheres of influence static void DrawSphere(window *W, Sphere *S) { int i; TimePoly *TP; image D; real Rad; if (!S) return; lock(S->polys); pencolour(W,&(S->rgb)); for (i=0;inum;i++) { TP= (*(S->polys)) + i; if (!SpansOverlap(&(TP->date),&(Step.intv),Opt.unc)) continue; StrokeIsland(W,TP->poly); } unlock(S->polys); if (S->loc.good) { Rad= fgtr(S->rad,50.0) * Proj->mag / EarthRad; if (Rad > 6.0 * Opt.size) { projectpoint(Proj,&(S->pos),&D); if (D.depth<-0.1) { fillcolour(W,&(S->rgb)); textcentre(W,D.pt.x,D.pt.y - (Opt.size / 2),S->name); } } } penindex(W,black); } void DrawSpheres(window *W) { int i; Sphere *S; textfont(W,"Geneva",Opt.size * 1.25); for (i=0;idate),&(Step.intv),Opt.unc)) continue; DrawSphere(W,S); } } // Drawing location hierarchy (debugging only) /* static void DrawHierarchyLine(Place *C) { Place *S; if (!C) return; S=C->sup; if (!S) return; if (C->rad>0) PenSize(2,2); MoveTo(C->pt.h,C->pt.v); LineTo(S->pt.h,S->pt.v); PenSize(1,1); } static void DrawPlaceHierarchy(void) { int i; Place *C; colour(orange); for (i=0;igood || !C->draw) continue; DrawHierarchyLine(C); } colour(black); }*/ // Location calculations static bool PlaceExistedThen(Place *C) { Place *Rep; if (!C) return 0; if (!SpansOverlap(&(C->when),&(Step.intv),Opt.unc)) return 0; Rep=C->rep; if (!Rep) return 1; if (DatesInOrder(&(Rep->when.start),&(Step.intv.end),Opt.unc)) return 0; return 1; } static real Threshold; // projected radius at which a place is deemed too small to draw static void PartVis(Place *C) { if (!C || !C->good) return; C->part=1; PartVis(C->sup); } static int SizeCategory(int RAD, bool HasSub, Place *Reg) { real Rad,Factor; int SS; SS=0; // to avoid warning if (Reg) { SS=Reg->size; if (SS==sizeOkay || SS==tooSmall) return tooSmall; } Rad= fgtr(RAD,50.0) * Proj->mag / EarthRad; if (Rad 6.0 * Threshold && RAD>50) return tooBig; return sizeOkay; } static void DoLocationCalc(Place *C) { image D; if (!C) return; C->draw=0; C->part=0; projectpoint(Proj,&(C->pos),&D); if (!D.good) { C->good=0; C->oth=0; return; } C->pt=D.pt; if (D.depth>-0.1) { C->good=0; C->oth=1; return; } C->good=1; C->stack=0; C->draw= PlaceExistedThen(C) || C->water || Opt.always; C->size=SizeCategory(C->rad,C->hassub,C->sup); if (C->size==sizeOkay && D.miss==0) PartVis(C->sup); } void DoLocationCalcs(void) { int i; Threshold= Opt.size * Opt.group * Opt.group / 8.0; for (i=0;iaka) return C->aka; return C->name; } static void DrawCity(window *W, Place *C) { fillindex(W,chestnut); beginpath(W); addcircle(W,C->pt.x,C->pt.y,1.5); fill(W); textsize(W,Opt.size); drawtext(W,C->pt.x + 4,C->pt.y - Opt.size + 2,MapName(C)); } static void DrawRegion(window *W, Place *C) { fillindex(W,C->water ? sapphire : black); if (C->rad>800) textsize(W,Opt.size * 1.25); else textsize(W,Opt.size * 1.1); textcentre(W,C->pt.x,C->pt.y - Opt.size + 2,MapName(C)); } void DrawLocations(window *W) { int i; Place *C; //DrawPlaceHierarchy(); for (i=0;idraw || C->size!=sizeOkay || C->waypt) continue; if (C->rad<1) DrawCity(W,C); else DrawRegion(W,C); } fillindex(W,black); } // Using places Place *CurrentPlace(const Place *C) { Place *Rep; if (!C) return NULL; Rep=C->rep; if (!Rep) return (Place *)C; if (DatesInOrder(&(Rep->when.start),&(Step.intv.end),Opt.unc)) return CurrentPlace(Rep); return (Place *)C; } Place *EffectivePlace(const Place *C) { Place *S; C=CurrentPlace(C); if (!C) return NULL; if (Opt.group==0/* nec? */ || C->size!=tooSmall) return (Place *)C; S=C->sup; if (!S || S->rad<=C->rad || S->size==tooBig) return (Place *)C; return EffectivePlace(S); } // Drawing travels void CalcLegPoint(point P1, point P2, real Frac, point *P) { P2.x-=P1.x; P1.x+= Frac * P2.x; P2.y-=P1.y; P1.y+= Frac * P2.y; if (P) *P=P1; } static Place *TravelPlace(const Place *C) { if (Opt.gpvoy) return EffectivePlace(C); return CurrentPlace(C); } static void DrawWayPoint(window *W, Place *C) { if (!C || !C->good) return; beginpath(W); addcircle(W,C->pt.x,C->pt.y,2); fill(W); C->size=sizeOkay; } static void DrawLimbLeg(window *W, point A, point B) { // doesn't work if B is !good for more than simply being beyond the limb vector V,Wv,P; setvector(&P,Proj->org.x,Proj->org.y,0.0); setvector(&V,A.x,A.y,0.0); vectordecr(&V,&P); setvector(&Wv,B.x,B.y,0.0); vectordecr(&Wv,&P); vectorincr(&V,&Wv); scalevector(&V,1.0 / Proj->mag); normalise(&V); scalevector(&V,Proj->mag); vectorincr(&V,&P); beginpath(W); movept(W,A); lineto(W,V.x,V.y); stroke(W); } static void DrawWholeLeg(window *W, const Place *C1, const Place *C2) { if (!C1 || !C2 || C1==C2) return; if (!C2->good) { if (C2->oth && C1->good) DrawLimbLeg(W,C1->pt,C2->pt); return; } if (!C1->good) { if (C1->oth) DrawLimbLeg(W,C2->pt,C1->pt); return; } beginpath(W); movept(W,C1->pt); linept(W,C2->pt); stroke(W); } static void DrawFracLeg(window *W, const Place *C1, const Place *C2, real F1, real F2) { point Pt; if (!C1 || !C1->good || !C2 || !C2->good) return; if (F1>=F2) return; beginpath(W); CalcLegPoint(C1->pt,C2->pt,F1,&Pt); movept(W,Pt); CalcLegPoint(C1->pt,C2->pt,F2,&Pt); linept(W,Pt); stroke(W); } static void DoDrawTravels(window *W, ArrDep *M, Progress *S, Progress *E) { int i,ST; Place *C1,*C2; if (!M || !S || !E) return; ST=S->leg; if (S->frac>0.0) ST++; for (i=ST;i<=E->leg;i++) { C1=M[i].city; C1=TravelPlace(C1); DrawWayPoint(W,C1); if (i>=E->leg) continue; C2=M[i + 1].city; C2=TravelPlace(C2); DrawWholeLeg(W,C1,C2); } if (S->leg==E->leg && S->frac>0.0) DrawFracLeg(W,S->c1,S->c2,S->frac,E->frac); else { if (S->frac>0.0) DrawFracLeg(W,S->c1,S->c2,S->frac,1.0); if (E->frac>0.0) DrawFracLeg(W,E->c1,E->c2,0.0,E->frac); } } void DrawTravels(window *W, Travels *T, int COLOUR) { ArrDep *M; colour C; if (!T) return; if (!SpansOverlap(&(Step.intv),&(T->time),Opt.unc)) return; // Is that the correct treatment of uncertainty here? lock(T->ad); M=*(T->ad); setcolour(&C,COLOUR); if (Opt.unc) { C.c[3]=0.35; pencolour(W,&C); fillcolour(W,&C); DoDrawTravels(W,M,&(T->ust),&(T->st)); DoDrawTravels(W,M,&(T->en),&(T->uen)); } C.c[3]=0.75; pencolour(W,&C); fillcolour(W,&C); DoDrawTravels(W,M,&(T->st),&(T->en)); unlock(T->ad); } bool GoodLeg(const Travels *T) { return T && T->st.c1 && T->st.c1->good && T->st.c2 && T->st.c2->good; } static void GroupPoint(Progress *P) { if (!P) return; P->c1=TravelPlace(P->c1); P->c2=TravelPlace(P->c2); if (P->c1==P->c2) P->frac=0.0; } void GroupTravels(Travels *T) { if (!T) return; GroupPoint(&(T->st)); GroupPoint(&(T->en)); GroupPoint(&(T->ust)); GroupPoint(&(T->uen)); }