// gentree.c // // by John D. de Boer #include "twist.h" extern int PEOPLE; extern Person ***People; // Dividing the name into two lines // P->gen1 != NULL is the indication that a name has already been divided static bool romnumbegins(const char *S) { if (!S) return 0; while (*S) { if (!firstocc(*S,"IVXLCD")) return 0; if (!*S || *S==' ') return 1; S++; } return 1; } static void PutUnbreakables(char *S) { char *P; bool Particle,WasSpace; if (!S) return; Particle=0; WasSpace=1; P=S; while (*P) { if (WasSpace) Particle=lowercase(*P); if (*P==' ') { WasSpace=1; if (Particle) *P=202; Particle=0; } // 202=xCA=nbsp else WasSpace=0; P++; } P=S; while (*P) { if (*P==' ' && romnumbegins(P + 1)) *P=202; P++; } } static void PutSpacesBack(char *S) { if (!S) return; while (*(S++)) if (((uchar)*S)==202) *S=' '; } static void DivideName(Person *P) { if (!P || !P->name || P->gen1) return; P->gen1=copyof(P->name); if (P->mingen<20.0) return;// PutUnbreakables(P->gen1); dividestring(&(P->gen1),&(P->gen2)); PutSpacesBack(P->gen1); PutSpacesBack(P->gen2); } // Application-defined parameters for drawing trees static genvars Gen; // local copy of drawing parameters void setgenparameters(const genvars *G) { if (!G) return; Gen=*G; } // "Tree" profiles static int sliceserial(long JD) { short Y; jdtodate(JD,&Y,NULL,NULL); if (Y>0) return (Y + (Gen.slice - 1)) / Gen.slice; return (Y + 1) / Gen.slice; } static treeprofile *newtreeprofile(int TOP, int BOTTOM, int WIDTH) { int i,L; treeprofile *T; tpelem *E; L= BOTTOM - TOP; if (L<0) return NULL; T=pointer(sizeof(treeprofile) + (L * sizeof(tpelem))); T->top=TOP; T->bottom=BOTTOM; for (i=0;irow + i; E->left= -WIDTH / 2; E->right= E->left + WIDTH; } return T; } static treeprofile *treeprofilecopy(treeprofile *T) { int i,L; treeprofile *C; if (!T) return NULL; L= T->bottom - T->top; if (L<0) return NULL; C=pointer(sizeof(treeprofile) + (L * sizeof(tpelem))); C->top=T->top; C->bottom=T->bottom; for (i=0;irow[i]=T->row[i]; return C; } static treeprofile *treeprofileappendright(treeprofile *A, treeprofile *B, bool Des, float *RIGHT) { int TOP,BOTTOM,L,SHIFT,T2,B2,i; treeprofile *C; if (RIGHT) *RIGHT=0; if (!A) return treeprofilecopy(B); if (!B) return treeprofilecopy(A); TOP=lsr(A->top,B->top); BOTTOM=gtr(A->bottom,B->bottom); L= BOTTOM - TOP; C=pointer(sizeof(treeprofile) + (L * sizeof(tpelem))); C->top=TOP; C->bottom=BOTTOM; SHIFT=0; if (Des) { T2=TOP; B2=lsr(A->bottom,B->bottom); } else { T2=gtr(A->top,B->top); B2=BOTTOM; } for (i=T2;itop; if (range(IA,A->bottom - A->top)) RA=A->row[IA].right; else RA=Gen.margin; IB= i - B->top; if (range(IB,B->bottom - B->top)) LB=B->row[IB].left; else LB=-Gen.margin; SHIFT=gtr(SHIFT,RA - LB); } *RIGHT=SHIFT; for (i=0;itop; RA=range(IA,A->bottom - A->top); if (RA) EA=A->row[IA]; IB= i + TOP - B->top; RB=range(IB,B->bottom - B->top); if (RB) { EB=B->row[IB]; EB.left+=SHIFT; EB.right+=SHIFT; } if (RA && RB) { C->row[i].left=lsr(EA.left,EB.left); C->row[i].right=gtr(EA.right,EB.right); } else if (RA) C->row[i]=EA; else if (RB) C->row[i]=EB; else { C->row[i].left=C->row[i].right=0; } } // better guess than zero? This occurs if A and B not contiguous in time. return C; } static treeprofile *treeprofileunion(treeprofile *A, treeprofile *B) { int TOP,BOTTOM,L,i; treeprofile *C; if (!A) return treeprofilecopy(B); if (!B) return treeprofilecopy(A); TOP=lsr(A->top,B->top); BOTTOM=gtr(A->bottom,B->bottom); L= BOTTOM - TOP; C=pointer(sizeof(treeprofile) + (L * sizeof(tpelem))); C->top=TOP; C->bottom=BOTTOM; for (i=0;itop; RA=range(IA,A->bottom - A->top); if (RA) EA=A->row[IA]; IB= i + TOP - B->top; RB=range(IB,B->bottom - B->top); if (RB) EB=B->row[IB]; if (RA && RB) { C->row[i].left=lsr(EA.left,EB.left); C->row[i].right=gtr(EA.right,EB.right); } else if (RA) C->row[i]=EA; else if (RB) C->row[i]=EB; else { C->row[i].left=C->row[i].right=0; } } return C; } static void centretreeprofile(treeprofile *T, short *RIGHT) { int i,L,SHIFT; tpelem E; if (RIGHT) *RIGHT=0; if (!T) return; L= T->bottom - T->top; if (L<0) return; E=T->row[0]; for (i=1;irow[i].left); E.right=gtr(E.right,T->row[i].right); } SHIFT= (E.left + E.right) / 2; if (RIGHT) *RIGHT=-SHIFT; for (i=0;irow[i].left-=SHIFT; T->row[i].right-=SHIFT; } } static treeprofile *boxprofile(window *W, Person *P) { int B,E,WIDTH; DivideName(P); P->gw1=textwidth(W,P->gen1); P->gw2=textwidth(W,P->gen2); WIDTH= P->gendid ? 10 : gtr(P->gw1,P->gw2); P->ght= Gen.font.leading + ((P->gen2 ? 2 : 1) * Gen.line) + 1; B=sliceserial(P->life.start.jd); E=sliceserial(P->life.start.jd + ((P->ght * 365.24) / Gen.scale)); return newtreeprofile(B,E + 1,WIDTH + Gen.space + (2 * Gen.margin)); } static treeprofile *ancestorspacer(Person *P) { int B,E,WIDTH,HEIGHT; WIDTH=0; HEIGHT=10; B=sliceserial(P->life.start.jd - ((HEIGHT * 365.24) / Gen.scale)); E=sliceserial(P->life.start.jd); return newtreeprofile(B,E + 1,WIDTH); } static treeprofile *descendantspacer(Person *P) { int B,E,WIDTH,HEIGHT; WIDTH=0; HEIGHT=10; B=sliceserial(P->life.start.jd + ((P->ght * 365.24) / Gen.scale)); E=sliceserial(P->life.start.jd + (((P->ght + HEIGHT) * 365.24) / Gen.scale)); return newtreeprofile(B,E + 1,WIDTH); } static void fillprofileleft(treeprofile *T, int L, int UPPER, int LOWER) { int A,B,i; if (!T) return; A=gtr(0,UPPER - T->top); B=lsr(LOWER - T->top,T->bottom - T->top); for (i=A;irow[i].left=lsr(L,T->row[i].left); } static void fillprofileright(treeprofile *T, int R, int UPPER, int LOWER) { int A,B,i; if (!T) return; A=gtr(0,UPPER - T->top); B=lsr(LOWER - T->top,T->bottom - T->top); for (i=A;irow[i].right=gtr(R,T->row[i].right); } void resetgendone(void) { int i; Person *P; for (i=0;igendid=0; } } static void resetgenser(void) { int i; Person *P; for (i=0;igenser=0; } } treeprofile *ancestorsprofile(window *W, Person *P) { treeprofile *Tree,*Box,*Temp,*M,*F; short RIGHT; if (!P) return NULL; Box=boxprofile(W,P); if (!Box) return NULL; if (P->gendid) return Box; P->gendid=1; P->moright=0; P->faright=0; if (!P->father && !P->mother) return Box; if (!P->father) F=NULL; else if (P->father->gendid) F=ancestorspacer(P); else F=ancestorsprofile(W,P->father); if (!P->mother) M=NULL; else if (P->mother->gendid) M=ancestorspacer(P); else M=ancestorsprofile(W,P->mother); //if (F && M && M->did) { old(M); M=NULL; } //if (F && M && F->did) { old(F); F=NULL; } if (M && F) Tree=treeprofileappendright(M,F,0,&(P->faright)); else Tree= F ? treeprofilecopy(F) : treeprofilecopy(M); if (!Tree) return Box; centretreeprofile(Tree,&RIGHT); Temp=treeprofileunion(Box,Tree); old(Box); old(Tree); Tree=Temp; P->moright+=RIGHT; P->faright+=RIGHT; Temp= M ? M : F; if (Temp) fillprofileleft(Tree,P->moright - Gen.margin,Temp->bottom,Box->top); Temp= F ? F : M; if (Temp) fillprofileright(Tree,P->faright + Gen.margin,Temp->bottom,Box->top); old(M); old(F); return Tree; } treeprofile *descendantsprofile(window *W, Person *P) { int i; treeprofile *Tree,*Box,*Temp,*De; Person *C; short RIGHT,ELDTOP,BENTOP,ELDRIGHT,BENRIGHT; float *Rt; bool Fem; if (!P) return NULL; Box=boxprofile(W,P); if (!Box) return NULL; if (P->gendid) return Box; P->gendid=1; if (P->nchild<1 || !P->children) return Box; Fem=Female(P); Tree=NULL; ELDTOP=BENTOP=Box->bottom; for (i=0;inchild;i++) { C=(*(P->children))[i]; if (!C) continue; if (C->gendid) De=descendantspacer(P); else De=descendantsprofile(W,C); if (!De) continue; if (i==0) ELDTOP=De->top; if (i == P->nchild - 1) BENTOP=De->top; Rt= Fem ? &(C->rightmo) : &(C->rightfa); if (!Tree) { Tree=De; *Rt=0; } else { Temp=treeprofileappendright(Tree,De,1,Rt); old(Tree); old(De); Tree=Temp; } } if (!Tree) return Box; centretreeprofile(Tree,&RIGHT); Temp=treeprofileunion(Box,Tree); old(Box); old(Tree); Tree=Temp; ELDRIGHT=RIGHT; BENRIGHT=RIGHT; for (i=0;inchild;i++) { C=(*(P->children))[i]; if (!C) continue; Rt= Fem ? &(C->rightmo) : &(C->rightfa); (*Rt)+=RIGHT; if (i==0) ELDRIGHT=*Rt; if (i == P->nchild - 1) BENRIGHT=*Rt; } fillprofileleft( Tree,ELDRIGHT - Gen.margin,Box->bottom - 1,ELDTOP); fillprofileright(Tree,BENRIGHT + Gen.margin,Box->bottom - 1,BENTOP); return Tree; } // Saving as HTML static void setserialup(Person *P, short *SERIAL) { if (!P || P->gendid || !SERIAL) return; P->genser=*SERIAL; (*SERIAL)++; P->gendid=1; setserialup(P->father,SERIAL); setserialup(P->mother,SERIAL); } static void setserialdown(Person *P, short *SERIAL) { int i; Person *C; if (!P || P->gendid || !SERIAL) return; P->genser=*SERIAL; (*SERIAL)++; P->gendid=1; if (!P->children) return; for (i=0;inchild;i++) { C=(*(P->children))[i]; setserialdown(C,SERIAL); } } static string coordstr(rect R, rect T) { string S; S=NULL; S=fig(left(R) - left(T)); S=cat3(5,S,",",fig(top(T) - top(R))); S=cat3(5,S,",",fig(right(R) - left(T))); S=cat3(5,S,",",fig(top(T) - bottom(R))); return S; } static void writeimagemap(Person *P, rect Total) { if (!P) return; writetextold(cat3(2,"genser),"\" shape=\"rect\" "),0); writetextold(cat3(2,"coords=\"",coordstr(P->genrect,Total),"\">"),10); } static void writeimagemapup(Person *P, rect Total) { if (!P || P->gendid) return; P->gendid=1; writeimagemap(P,Total); writeimagemapup(P->father,Total); writeimagemapup(P->mother,Total); } static void writeimagemapdown(Person *P, rect Total) { int i; Person *C; if (!P || P->gendid) return; P->gendid=1; if (P->genser>1) writeimagemap(P,Total); if (!P->children) return; for (i=0;inchild;i++) { C=(*(P->children))[i]; writeimagemapdown(C,Total); } } static void writehtml(Person *P) { if (!P) return; writetextold(cat5(2,"

genser),"\">",P->name,"

"),10); writetextold(MakeHTMLInfo(P),10); } static void writehtmlup(Person *P) { if (!P || P->gendid) return; P->gendid=1; writehtml(P); writehtmlup(P->father); writehtmlup(P->mother); } static void writehtmldown(Person *P) { int i; Person *C; if (!P || P->gendid) return; P->gendid=1; if (P->genser>1) writehtml(P); if (!P->children) return; for (i=0;inchild;i++) { C=(*(P->children))[i]; writehtmldown(C); } } void genhtml(window *W, Person *P) { rect Total; bool Ok; file F; short SERIAL; string Title,WH; if (!W || !P) return; if (!W->pdfsize) return; W->pdfsize(W,&Total); Title=cat(0,P->name,"'s Family Tree"); modaltextentry("Please provide a title for the web page:",&Title); savepdf(W,"tree.pdf",1); Ok=saveas("Save HTML File As","Untitled.html",'****','text',&F); if (!Ok) return; setpath(&F); resetgenser(); SERIAL=1; resetgendone(); setserialup(P,&SERIAL); resetgendone(); setserialdown(P,&SERIAL); P->genser=1; writetextold(cat3(0,"",Title,""),10); writetext("",10); writetext("",10); writetextold(cat3(0,"

",Title,"

\n"),10); WH=cat4(10,"width=",fig(width(Total))," height=",fig(height(Total))); writetextold(cat3(2,"

"),10); writetext("",10); resetgendone(); writeimagemapup(P,Total); resetgendone(); writeimagemapdown(P,Total); writetext("\n",10); resetgendone(); writehtmlup(P); resetgendone(); writehtmldown(P); writetext("",10); closefile(&F); notification("Please consult the instructions for important information on graphics formats."); }