// drawmap.c // // This file draws the historical objects (people/events) on the map // // 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; //bool MapMono; // whether map will be drawn in black & white rect MapPage; // the bounds of the current view projection *Proj; // projection of map currently being drawn static float Spacing; // total line spacing static int SIDESTACK; // the number of things piled up so far along the lower-left // Drawing icons on the map static const int MAXSTACK = 24; // maximum number of things to show in a stack static bool SidePosition(point *P) { int POS; if (!P) return 0; POS=SIDESTACK++; if (POS>=MAXSTACK) return 0; P->x= left(MapPage) + 20; P->y= bottom(MapPage) + 22 + (20 * POS); return 1; } static bool MapPosition(Place *C, point *P) { int POS; if (!C || !C->good || !P) return 0; POS=C->stack++; if (POS>=MAXSTACK) return 0; *P=C->pt; P->x+= 10 + (Spacing * POS); P->y+= 4 + (Spacing * POS); return 1; } static void MapIcon(window *W, point P, picture Im, const char *S, void *Obj, bool Fade) { if (Fade) CGContextSetAlpha(W->q2d,0.5); DrawIcon(W,Im,P,Opt.size / 10.0); CGContextSetAlpha(W->q2d,1.0); if (S) { P.x+=2.0; fillindex(W,Obj==Sel ? crimson : rust); LineMapText(W,P,S,Obj); } } static bool ShouldDraw(Place *C, const Travels *T, point *P) { if (T && T->st.frac>0.0) return GoodLeg(T); if (!C || !P) return 0; if (C->size==tooBig) { if (!T || !SpansOverlap(&(Step.intv),&(T->time),Opt.unc)) return C->part && Opt.region && SidePosition(P); } return MapPosition(C,P); } static void CalcLegPosn(Travels *T, point *P) { if (!T || !GoodLeg(T) || !P) return; CalcLegPoint(T->st.c1->pt,T->st.c2->pt,T->st.frac,P); P->x+=8; P->y+=2; } // Misc static bool AppearsOnMap(const Place *C) { if (!C) return 0; if (C->good && pointinrect(C->pt,MapPage)) return 1; if (C->part && Opt.region) return 1; return 0; } static Place *ItemsPlace(const Place *C, const Travels *T) { if (!T) return EffectivePlace(C); C=T->st.c1; if (SpansOverlap(&(Step.intv),&(T->time),Opt.unc)) return CurrentPlace(C); return EffectivePlace(C); } static bool PossiblyNot(const Span *L) { Span S; S=*L; S.start.jd+=S.start.unc; S.end.jd-=S.end.unc; if (S.start.jdStep.intv.end.jd; } // Drawing People static bool ReignsDuringStep(const Person *P) { int i; if (!P) return 0; for (i=0;inrgn;i++) if (SpansOverlap(P->reign + i,&(Step.intv),Opt.unc)) return 1; return 0; } static void DoPersonCalcs(Person *P, bool *OnMap) { Place *C; if (!P) return; P->draw=0; P->fade=0; if (OnMap) *OnMap=0; // if no overlap with entire time-line window, reject here? if (P->trav) { DoTravelCalcs(P->trav); GroupTravels(P->trav); } C=ItemsPlace(P->home,P->trav); if (!C) return; if (OnMap) *OnMap=AppearsOnMap(C); if (!SpansOverlap(&(P->life),&(Step.intv),Opt.unc)) return; if (P->nrgn>0 && Opt.term && !ReignsDuringStep(P)) return; P->draw=ShouldDraw(C,P->trav,&(P->pt)); if (P->draw && Opt.unc && PossiblyNot(&(P->life))) P->fade=1; } static string AddAge(Person *P) { short YEAR,YOB,MOB,DOB,AGE,UNC; char *S; if (!P) return NULL; jdtodate(P->life.start.jd,&YOB,&MOB,&DOB); if (P->life.start.jd0) ? fig(YOB) : cat(1,fig(-YOB)," B.C."); S=cat(2,"b. ",S); } if (Opt.unc) { UNC= (P->life.start.unc / 365.25) + 0.5; if (UNC>0) S=cat3(5,S," ± ",fig(UNC)); } return cat3(2," (",S,")"); } static void DrawPerson(window *W, Person *P) { char *S; S=copyof(Opt.fullnam ? P->name : P->surname); if (Opt.age) S=cat(3,S,AddAge(P)); MapIcon(W,P->pt,PersonIcon(P),S,P,P->fade); old(S); } // Drawing Events static void DoEventCalcs(Event *E, bool *OnMap) { Place *C; if (!E) return; E->draw=0; E->fade=0; if (OnMap) *OnMap=0; if (E->trav) { DoTravelCalcs(E->trav); GroupTravels(E->trav); } C=ItemsPlace(E->place,E->trav); if (!C) return; if (OnMap) *OnMap=AppearsOnMap(C); if (!SpansOverlap(&(E->date),&(Step.intv),Opt.unc)) return; E->draw=ShouldDraw(C,E->trav,&(E->pt)); if (E->draw && Opt.unc && PossiblyNot(&(E->date))) E->fade=1; } static void DrawEvent(window *W, Event *E) { MapIcon(W,E->pt,EventIcon(E),E->name,E,E->fade); } // General drawing stuff static void DoCalcs(MapInfo *D) { int i; void *V; Person *P; Event *E; bool Vis; if (!D) return; SIDESTACK=0; Spacing= 1.2 * Opt.size; minimumsize((D->vis),ORDER,sizeof(bool)); for (i=0;ivis))[i]=Vis; } else if (VoidIsEvent(V)) { E=(Event *)V; DoEventCalcs(E,&Vis); (*(D->vis))[i]=Vis; } } } static void DrawTravelsLayer(window *W) { int i; void *V; Person *P; Event *E; bool SelSeen; savegraphics(W); penwidth(W,2); SelSeen=0; for (i=0;itrav) DrawTravels(W,P->trav,crimson); } else if (VoidIsEvent(V)) { E=(Event *)V; if (E->trav) DrawTravels(W,E->trav,ultramarine); } } if (Sel && SelSeen) { if (VoidIsPers(Sel)) { P=(Person *)Sel; if (P->trav) DrawTravels(W,P->trav,red); } else if (VoidIsEvent(Sel)) { E=(Event *)Sel; if (E->trav) DrawTravels(W,E->trav,blue); } } restoregraphics(W); } static void DrawThings(window *W) { int i; void *V; Person *P; Event *E; textfont(W,"Geneva",Opt.size); for (i=0;idraw && P->trav && P->trav->st.frac>0.0) { CalcLegPosn(P->trav,&(P->pt)); DrawPerson(W,P); } } else if (VoidIsEvent(V)) { E=(Event *)V; if (E->draw && E->trav && E->trav->st.frac>0.0) { CalcLegPosn(E->trav,&(E->pt)); DrawEvent(W,E); } } // doesn't work? } for (i= ORDER - 1;i>=0;i--) { V=(*Order)[i]; if (VoidIsPers(V)) { P=(Person *)V; if (P->draw && (!P->trav || P->trav->st.frac==0.0)) DrawPerson(W,P); } else if (VoidIsEvent(V)) { E=(Event *)V; if (E->draw && (!E->trav || E->trav->st.frac==0.0)) DrawEvent(W,E); } } } // Entry routines for drawing static void DrawTheMap(window *W, rect R) { if (!W) return; MapPage=R; Proj=W->proj; if (!Proj) return; DrawGlobe(W); DrawSpheres(W); DoLocationCalcs(); DoCalcs(W->appdata); if (Opt.trip>0) DrawTravelsLayer(W); DrawLocations(W); DrawThings(W); DrawLegends(W); } void DrawMap(window *W) { DrawTheMap(W,W->page); } void PrintMap(window *W, short PAGE) { rect Save; if (!W || !Proj) return; Save=Proj->port; resizeproj(Proj,W->page); // make these unnecessary DrawTheMap(W,W->page); // make 2nd arg unnec resizeproj(Proj,Save); } // Clicking in maps static bool StringHit(window *W, point Pt, const char *S) { float L; rect R; L=textwidth(W,S); setrect(&R,2,-2,L,Opt.size); return pointinrect(Pt,R); } static bool PersonMapHit(window *W, point Pt, Person *P) { if (!W || !P || !P->draw) return 0; Pt.x-=P->pt.x; Pt.y-=P->pt.y; return StringHit(W,Pt,Opt.fullnam ? P->name : P->surname) || BitmapHit(PersonBitmap(P),Pt,Opt.size / 10.0); } static bool EventMapHit(window *W, point Pt, Event *E) { if (!W || !E || !E->draw) return 0; Pt.x-=E->pt.x; Pt.y-=E->pt.y; return StringHit(W,Pt,E->name) || BitmapHit(EventBitmap(E),Pt,Opt.size / 10.0); } static void *VoidMapHit(window *W, point Pt) { int i; void *V; if (!W) return NULL; for (i=0;iwind; if (!W) return NULL; DoLocationCalcs(); DoCalcs(M->wind->appdata); textfont(W,"Geneva",Opt.size); V=VoidMapHit(W,M->point); return V; } // Map focus static void LegFocus(Travels *T) { vector V; if (!T || !T->st.c1 || !T->st.c2) return; vectorcomb(&(T->st.c1->pos),T->st.frac,&(T->st.c2->pos),&V); scalevector(&V,-1.0); setdirection(Proj,&V); } static void PlaceFocus(Place *C) { vector V; if (!C) return; V=C->pos; scalevector(&V,-1.0); setdirection(Proj,&V); } static void PersonFocus(Person *P) { Travels *T; if (!P) return; T=P->trav; if (T && T->st.frac>0.0) LegFocus(T); else PlaceFocus(ItemsPlace(P->home,P->trav)); } static void EventFocus(Event *E) { Travels *T; if (!E) return; T=E->trav; if (T && T->st.frac>0.0) LegFocus(T); else PlaceFocus(ItemsPlace(E->place,E->trav)); } static void ItemFocus(void *V) { if (VoidIsPers(V)) PersonFocus(V); else if (VoidIsEvent(V)) EventFocus(V); } void MapFocus(window *W) { if (!W || !Sel) return; Proj=W->proj; if (!Proj) return; DoLocationCalcs(); DoCalcs(W->appdata); ItemFocus(Sel); }