// drawglobe.c // // This file draws the polygonal outlines of the continents and fills them. // // by John D. de Boer #include "twist.h" #include "twindow.h" extern TimeGlobs Time; extern GlobalOpts Opt; extern StepGlobs Step; extern rect MapPage; extern vectorpolys **TTTPoly; extern const real EarthRad; static vectorpolys **ResPoly; // the polygons read from internal resources static colour WaterBlue; // colour of water // Loading the earth polygon static void LoadEarthPoly(void) { polygons **Py; Py=newpolys(); if (!readrespolys(Py,"earth polygons")) return; ResPoly=longtovectorpolys(Py); oldpolys(Py); } // Drawing the background, disk & earth polygon static bool PenDown, // whether the last line drawn was visible Begun, // whether the polygon has been started with a MoveTo() BeenOff; // whether pen has been up before beginning //static point Pen; // ending pen location if PenDown static void GoDiscColour(window *W, bool Water) { if (!Opt.fill) { fillindex(W,white); return; } if (Water) { fillcolour(W,&WaterBlue); return; } fillindex(W,beige); } static void CoastLine(window *W, const vector *V) { image D; if (!W || !V) return; projectpoint(W->proj,V,&D); if (D.depth>0.0) { PenDown=0; return; } movetolineto(W,&D,&PenDown); /*if (PenDown) Pen=D.pt;*/ } void StrokeIsland(window *W, vectorpoly **P) { MapInfo *D; int i,NUM; vectorpoly *MP; if (!W || !P || !*P) return; D=W->appdata; if (!D) return; if (!sphererectsect(&(D->sphere.bound.max),&((*P)->rect))) return; beginpath(W); PenDown=0; lock((void *)P); MP=*P; NUM=MP->num; for (i=0;ipt + i); unlock((void *)P); stroke(W); } static void MakeRadiantPoint(point P, point C, point *Q) { Q->x= C.x + (3.0 * (P.x - C.x)); Q->y= C.y + (3.0 * (P.y - C.y)); } /* static void CoastFillLine(window *W, const vector *V) { image D; point P; projection *Proj; if (!W) return; Proj=W->proj; if (!Proj) return; projectpoint(Proj,V,&D); if (!D.good) return; if (D.depth>0.0) { if (PenDown) { MakeRadiantPoint(D.pt,Proj->org,&P); linept(W,P); } PenDown=0; BeenOff=1; return; } if (PenDown) { linept(W,D.pt); PenDown=1; return; } MakeRadiantPoint(D.pt,Proj->org,&P); if (!Begun && !BeenOff) movept(W,D.pt); else { if (Begun) linept(W,P); else movept(W,P); linept(W,D.pt); } Begun=1; PenDown=1; }*/ static void CoastFillLine(window *W, const vector *V) { image D; point P; projection *Proj; if (!W) return; Proj=W->proj; if (!Proj) return; projectpoint(Proj,V,&D); if (!D.good) return; if (D.depth>0.0) { if (PenDown) { MakeRadiantPoint(D.pt,Proj->org,&P); linept(W,P); } PenDown=0; BeenOff=1; return; } if (PenDown) { linept(W,D.pt); PenDown=1; return; } if (Begun || BeenOff) { MakeRadiantPoint(D.pt,Proj->org,&P); if (Begun) linept(W,P); else movept(W,P); linept(W,D.pt); } else movept(W,D.pt); Begun=1; PenDown=1; } static void FillIsland(window *W, vectorpoly **P) { MapInfo *D; int i,NUM; vectorpoly *MP; if (!W || !P || !*P || (*P)->open) return; D=W->appdata; if (!D) return; if (!sphererectsect(&(D->sphere.bound.max),&((*P)->rect))) return; beginpath(W); PenDown=0; Begun=0; BeenOff=0; lock((void *)P); MP=*P; NUM=MP->num; for (i=0;ipt + i); unlock((void *)P); fill(W); } static void DrawEarthPoly(window *W) { int i; point P; projection *Proj; vectorpolys **Use; vectorpoly **VP; if (!W) return; Proj=W->proj; if (!Proj) return; P=middle(W->page); savegraphics(W); fillindex(W,Opt.fill ? black : white); fillrect(W,W->page); beginpath(W); addcircle(W,P.x,P.y,Proj->mag); if (Opt.fill) { GoDiscColour(W,1); fill(W); beginpath(W); addcircle(W,P.x,P.y,Proj->mag); clip(W); } else { penindex(W,chestnut); stroke(W); } Use= TTTPoly ? TTTPoly : ResPoly; for (i=0;i<(*Use)->num;i++) { VP=(*Use)->poly[i]; if (Opt.fill) { GoDiscColour(W,(*VP)->lake); FillIsland(W,VP); } penindex(W,chestnut); StrokeIsland(W,VP); } restoregraphics(W); } // Drawing the coordinate lines & ticks static void SphereToView(const projection *P, const vector *V, image *D) { projontosphere(P,V,D); } static void SetSphereTask(window *W) { MapInfo *D; if (!W) return; D=W->appdata; if (!D) return; initspheretask(&(D->sphere),W,W->proj,1,5,&SphereToView,0); setcolour(&(D->sphere.c2nd),brown); setcolour(&(D->sphere.c1st),chestnut); } static void DrawCoords(window *W) { MapInfo *D; if (!W) return; D=W->appdata; if (!D) return; drawsphericalcoords(&(D->sphere)); penindex(W,oxblood); drawequator(&(D->sphere)); drawprimemeridian(&(D->sphere),1); penindex(W,black); } static void DrawTicks(window *W) { MapInfo *MI; spheretask *ST; projection *Proj; int i; image D; vector U; char *S; bool North; if (!W) return; MI=W->appdata; if (!MI) return; ST=&(MI->sphere); Proj=W->proj; if (!Proj) return; if (Proj->mag<150.0) return; fillindex(W,oxblood); textfont(W,"Geneva",Opt.size); North= Proj->elev < 0.0; for (i= -10800 + ST->longintv;i<=10800;i+=ST->longintv) { coordtick(ST,ST->ticklat,i,&D,North ? 'n' : 's',&U); if (!D.good) continue; scalevector(&U,12); S=cat(1,fig(abso(i / 60)),"¡"); textcentre(W,D.pt.x + U.x,D.pt.y + U.y - 6,S); old(S); } for (i= -5400 + ST->latintv;i<= 5400 - ST->latintv;i+=ST->latintv) { coordtick(ST,i,ST->ticklong,&D,'e',&U); if (!D.good) continue; scalevector(&U,15); S=cat(1,fig(abso(i / 60)),"¡"); textcentre(W,D.pt.x + U.x,D.pt.y + U.y - (Opt.size / 2.0),S); old(S); } } // Drawing the globe void DrawGlobe(window *W) { if (!W) return; setlookpoint(W->proj,0.0,0.0,0.0); SetSphereTask(W); DrawEarthPoly(W); if (Opt.coord) { DrawCoords(W); DrawTicks(W); } } // Legends static string YearString(int Y, bool Era) { if (Y<0) return Era ? cat(1,fig(abso(Y))," B.C.") : fig(abso(Y)); return Era ? cat(2,"A.D. ",fig(Y)) : fig(Y); } static string YMDString(void) { long JD1,JD2; short Y1,M1,D1,Y2,M2,D2; char *S; bool Mos,Days,MosOfDiffYears; JD1=Time.jd; jdtodate(JD1,&Y1,&M1,&D1); JD2=Step.intv.end.jd; jdtodate(JD2,&Y2,&M2,&D2); Mos=(Step.days<700); Days=(Step.days<50); S=YearString(Y1,1); if (Mos) { S=cat3(1,S," ",monthname(M1)); if (Days) S=cat3(1,S," ",fig(D1 + 1)); } if (JD1==JD2) return S; S=cat(1,S," to"); MosOfDiffYears= (Y1!=Y2) && Mos; if (Y2!=Y1) S=cat3(5,S," ",YearString(Y2,Y1<0 || Y2<0 || MosOfDiffYears)); if (Mos && (Y1!=Y2 || M1!=M2)) S=cat3(1,S," ",monthname(M2)); if (Days) S=cat3(1,S," ",fig(D2 + 1)); return S; } static string IncrementString(void) { char *S; S=fig(Step.incr); switch (Step.unit) { case stepCentury: return cat(1,S,"-century step"); case stepDecade: return cat(1,S,"0-year step"); case stepYear: return cat(1,S,"-year step"); case stepMonth: return cat(1,S,"-month step"); case stepWeek: return cat(1,S,"-week step"); case stepDay: return cat(1,S,"-day step"); } return S; } static string ProjGeoRefString(const projection *P) { real X; GeoRef G; if (!P) return NULL; X=-P->elev; X*= 60.0 * fiftyseven; if (X>0.0) X+=0.5; else X-=0.5; G.lat=X; X=modtwopisym(P->azim - pi); X*= 60.0 * fiftyseven; if (X>0.0) X+=0.5; else X-=0.5; G.lng=X; G.good=1; return GeoRefString(&G); /*real Q; char *S,*Hemi; if (!P) return NULL; Q=fabs(P->elev); Hemi= (P->elev<0.0) ? " N" : (P->elev>0.0) ? " S" : NULL; S=cat3(2,"Lat: ",degminstring(Q,1),Hemi); Q=modtwopisym(pi - P->azim); Hemi= (Q<0.0) ? " E" : (Q>0.0) ? " W" : NULL; return cat4(5,S," Long: ",degminstring(fabs(Q),1),Hemi);*/ } static string ScaleString(const projection *P) { long Q; if (!P) return NULL; Q= EarthRad * P->width / P->mag; if (Q>=1000) { Q/=100; return (cat4(5,fig(Q / 10)," ",fig(Q % 10),"00 km")); } if (Q>=100) { Q/=10; return (cat(1,fig(Q),"0 km")); } return cat(1,fig(Q)," km"); } void DrawLegends(window *W) { rect R; char *S; float Size; if (!W) return; Size=Opt.size; R=W->page; inset(&R,5); R.size.height-=Size; fillindex(W,rust); //textfont(W,"Chicago",1.15 * Size); S=YMDString(); drawtext(W,left(R),top(R),S); old(S); textfont(W,"Geneva",Size); S=IncrementString(); drawtext(W,left(R),bottom(R),S); old(S); S=ScaleString(W->proj); textleft(W,right(R),top(R),S); old(S); S=ProjGeoRefString(W->proj); textleft(W,right(R),bottom(R),S); old(S); } // Initialisation void InitDrawGlobe(void) { ResPoly=NULL; //TTTPoly.loaded=0; LoadEarthPoly(); if (!ResPoly) halt("Can't load earth polygon TEXT resource"); setrgb(&WaterBlue,0.40,0.75,1.0); }