#include"tree.hpp" #include"utils.hpp" #include #include #include using std::ofstream; using std::ifstream; using std::vector; Tree Tree::operator+ (const Tree& other) const { Tree result(*this); return result += other; } Tree& Tree::operator+=(const Tree& other) { vector newId; //The indexes people from II tree will have in I tree newId.reserve(other.numPeople); U32 idCounter = numPeople; for (person_id i = 0, j; i < other.numPeople; ++i) //find the new ids for people in II, people present in both trees get the ids from I { for (j = 0; j < numPeople; ++j) { if (other.people[i] == people[j]) { newId[i] = j; break; } } if (j >= numPeople) /* not present in I */ newId[i] = idCounter++; } people.reserve(idCounter); for (person_id i = 0; i < other.numPeople; ++i) /* add the personal data of II to I */ { if (newId[i] >= numPeople) /* isnt already in I */ people.push_back(other.people[i]); } relations.reserve(idCounter); for (person_id i = 0; i < other.numPeople; ++i) //merge the relative data { if (newId[i] >= numPeople) /* isnt already in I */ { relations.push_back(other.relations[i]); for (Relation& rel: relations.back()) rel.id = newId[rel.id]; /* for (person_id j = 0; j < other.relations[i].size(); ++j) */ /* relations[newId[i]][j].id = newId[ relations[newId[i]][j].id ]; */ } else //if present in both trees { /* for (person_id j = 0; j < other.relations[i].size(); ++j) //combine relations */ /* { */ /* if (newId[other.relations[i][j].id] >= numPeople) */ /* ++people[newId[i]].numRel_; */ /* } */ person_id idIn1 = newId[i]; relations[idIn1].reserve(relations[idIn1].size() + other.relations[i].size()); for (Relation rel: other.relations[i]) { if(newId[rel.id] >= numPeople) /* not already in I */ relations[idIn1].push_back( {newId[rel.id], rel.type} ); else { } } } } numPeople = idCounter; //Number of members in I tree return *this; } Tree& Tree::operator-= (const Tree& other) { for (U32 i = 0; i < numPeople; ++i) { for (const Person& othPers: other.people) { if (people[i] == othPers) { removePerson(i); --i; break; } } } return *this; } void Tree::rename(const char* newName) { treeName = newName; } person_id Tree::findId(const char* name, bool m) const { Person sought(name, {}, m); for (person_id i = 0; i < numPeople; ++i) { if (sought == people[i]) return i; } return Nobody; } person_id Tree::findOldestAncestor(person_id id, bool isMale) const { for (const Relation& rel: relations[id]) { if (rel.type == Child && people[ rel.id ].isMale() == isMale) return findOldestAncestor(rel.id, isMale); } return id; } person_id Tree::findCommonAncestor(const unsigned first, const unsigned second) const { vector visited(numPeople); //if a member is visited twice he's a common ancestor commonAncestorRec(first, visited); commonAncestorRec(second, visited); person_id oldest = Nobody; U16 oldestBday = SHRT_MAX; for (person_id i = 0; i < numPeople; ++i) { if (visited[i] == 2 && people[i].birth().year < oldestBday) { oldestBday = people[i].birth().year; oldest = i; } } return oldest; } person_id Tree::findParent(person_id child, bool isParentMale) const { person_id parent = Nobody; for(const Relation& rel: relations[child]) { if (rel.type == Child && people[rel.id].isMale() == isParentMale) parent = rel.id; } return parent; } vector Tree::findChildren(person_id person) const { if(person == Nobody) return {}; vector children; for(const Relation& rel: relations[person]) { if (rel.type == Parent) children.push_back(rel.id); } return children; } vector Tree::findChildren(person_id person, person_id exclude) const { vector children = findChildren(person); for(U32 i = 0; i < children.size(); ++i) { if(children[i] == exclude) remove(children, i); } return children; } person_id Tree::findSpouse(person_id person) const { person_id spouse = Nobody; for(const Relation& rel: relations[person]) { if (rel.type == Spouse) { spouse = rel.id; break; } } return spouse; } RelType Tree::findRelation(person_id first, person_id second) const { const auto& curRels = relations[first]; for(const Relation& rel: curRels) { if (rel.id == second) return rel.type; } return None; } void Tree::saveToFile() const { ofstream file(treeName + ".famt", std::ios::binary); file.write((const char*)(&numPeople), sizeof(numPeople)); for (const Person& p: people) p.write(file); for (auto& rels: relations) { U32 numRels = rels.size(); file.write((const char*)(& numRels), sizeof(numRels)); file.write((const char*)(rels.data()), sizeof(Relation) * numRels); } } bool Tree::loadFromFile() { ifstream file(treeName + ".famt", std::ios::binary); file.read((char*)(&numPeople), sizeof(numPeople)); people.resize(numPeople); for (Person& p: people) p.read(file); relations.resize(numPeople); for (auto& rels: relations) { U32 numRels; file.read((char*)(& numRels), sizeof(numRels)); rels.resize(numRels); file.read((char*)(rels.data()), sizeof(Relation) * numRels); } return true; } void Tree::addPerson(const char* name, bool isMale, unsigned father, unsigned mother, EventTime birth, EventTime death) { people.emplace_back(name, birth, isMale, death); relations.emplace_back(); /* relations of the new person */ ++numPeople; person_id new_person_id = numPeople - 1; addRelation(father, Parent, new_person_id); addRelation(mother, Parent, new_person_id); updateRels(new_person_id); } bool Tree::addRelation(const char* firstName, const RelType type, const char* secondName) { return addRelation(findId(firstName), type, findId(secondName)); } bool Tree::addRelation(const unsigned first, const RelType type, const unsigned second, bool update) { if (first == Nobody || first >= numPeople || second == Nobody || second >= numPeople || type == None) return false; switch (type) { case Parent: addRelOneSide(first, Parent, second); addRelOneSide(second, Child, first); break; case Child: addRelOneSide(first, Child, second); addRelOneSide(second, Parent, first); break; case Sibling: case HalfSibling: case Spouse: case ExSpouse: addRelOneSide(first, type, second); addRelOneSide(second, type, first); break; default: assert(!"Unexpected case"); break; } if(update) { updateRels(first); updateRels(second); } return true; } void Tree::addRelOneSide(person_id first, RelType type, person_id second) { unsigned i = 0; for (; i < relations[first].size(); ++i) //Check if they are already relatives { if (relations[first][i].id == second) { relations[first][i].type = type; break; } } if (i >= relations[first].size()) { relations[first].push_back({second, type}); } } void Tree::removeRelation(const char* firstName, const char* secondName) { removeRelation(findId(firstName), findId(secondName)); } void Tree::removePerson(const person_id id) { person_id relId; //Íoìåð íà òåêóùèÿ ðîäíèíàòà for (Relation rel: relations[id]) //Remove all of his relations { relId = rel.id; for (U32 j = 0; j < relations[relId].size(); ++j) { if (relations[relId][j].id == id) { remove(relations[relId], j); break; } } } for (unsigned i = 0; i < relations[numPeople - 2].size(); ++i) //Move the last in his place and update IDs { relId = relations[numPeople - 1][i].id; for (unsigned j = 0; j < relations[relId].size(); ++j) { if (relations[relId][j].id == numPeople - 1) { relations[relId][j].id = id; break; } } } remove(relations, id); remove(people, id); --numPeople; } /* void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) */ /* { */ /* removePerson(findId(personName, year, month, day)); */ /* } */ void Tree::removeRelation(const unsigned first, const unsigned second) { for (unsigned i = 0; i < relations[first].size(); ++i) { if (relations[first][i].id == second) { remove(relations[first], i); break; } } for (unsigned i = 0; i < relations[second].size(); ++i) { if (relations[second][i].id == first) { remove(relations[second], i); break; } } } static void displayHelp() { WINDOW* help_tab = newwin(LINES, COLS, 0, 0); waddstr(help_tab, "Enter - focus the currently selected person\n" "Arrow keys - move the camera\n" "H, J, K, L - move the selection\n" "I - display info about the selected person\n"); mvwaddstr(help_tab, LINES-1, 0, "Press any key to return."); wrefresh(help_tab); getch(); /* wait press */ werase(help_tab); wrefresh(help_tab); /* todo: dont update */ delwin(help_tab); } void Tree::display() { draw(); /* todo: draw every time */ I32 c; do { c = std::toupper(getch()); switch(c) { case KEY_F(1): displayHelp(); draw(); break; case '\n': focused_ = selected_; draw(); break; case KEY_LEFT: ++offsetX_; draw(); break; case KEY_RIGHT: --offsetX_; draw(); break; case KEY_DOWN: --offsetY_; draw(); break; case KEY_UP: ++offsetY_; draw(); break; case 'H': selectLeft(); break; case 'J': selectDown(); break; case 'K': selectUp(); break; case 'L': selectRight(); break; case 'I': people[selected_].displayInfo(); draw(); break; default: break; } } while(c != 'W'); } void Tree::updateRels(person_id id) { person_id dad = findParent(id, true); person_id mom = findParent(id, false); auto dad_children = findChildren(dad, id); auto mom_children = findChildren(mom, id); for(U32 i = 0; i < dad_children.size(); ++i) { for(U32 j = 0; j < mom_children.size(); ++j) { /* full sibling found, add and continue */ if (dad_children[i] == mom_children[j]) { addRelation(id, Sibling, dad_children[i], false); remove(dad_children, i); remove(mom_children, j); --i; break; } } } for(U32 i = 0; i < dad_children.size(); ++i) addRelation(id, HalfSibling, dad_children[i], false); for(U32 i = 0; i < mom_children.size(); ++i) addRelation(id, HalfSibling, mom_children[i], false); } void Tree::draw() const { erase(); //Clear the screen wnoutrefresh(stdscr); I16 drawY = offsetY_/* (LINES) / 2 */; I16 drawX = offsetX_/* (COLS) / 2 */; drawLineWithWives(focused_, drawY, drawX); doupdate(); } void Tree::print() const { for(const Person& member: people) member.displayInfo(); } /* void Tree::printRel(const unsigned id)const */ /* { */ /* cout << people[id].name_; */ /* if (people[id].numRel_ == 0) */ /* cout << " has no known relatives"; */ /* else */ /* { */ /* unsigned relId; */ /* cout << "'s relatives:\n"; */ /* for (unsigned i = 0; i < people[id].numRel_; ++i) */ /* { */ /* relId = relatives[id][i]; */ /* cout << people[relId].name_ << " - "; */ /* switch (relations[id][i]) */ /* { */ /* case Father: */ /* case Mother: */ /* cout << (people[relId].isMale_ ? "son" : "daughter"); */ /* break; */ /* case Son: */ /* case Daughter: */ /* cout << (people[relId].isMale_ ? "father" : "mother"); */ /* break; */ /* case Brother: */ /* case Sister: */ /* cout << (people[relId].isMale_ ? "brother" : "sister"); */ /* break; */ /* case HalfBrother: */ /* case HalfSister: */ /* cout << (people[relId].isMale_ ? "half brother" : "half sister"); */ /* break; */ /* case Husband: */ /* cout << "wife"; */ /* break; */ /* case Wife: */ /* cout << "husband"; */ /* break; */ /* case ExHusband: */ /* cout << "ex-wife\n"; */ /* break; */ /* case ExWife: */ /* cout << "ex-husband\n"; */ /* break; */ /* } */ /* cout << '\n'; */ /* } */ /* cout << '\n'; */ /* } */ /* } */ void Tree::printMember(const unsigned id)const { people[id].displayInfo(); } void Tree::printOldestAncestors(const unsigned id)const { people[findOldestAncestor(id)].displayInfo(); people[findOldestAncestor(id, 1)].displayInfo(); } void Tree::commonAncestorRec(person_id id, vector &visited) const { ++visited[id]; auto& curRels = relations[id]; for (const Relation& rel: curRels) { if (rel.type == Child) { ++visited[rel.id]; commonAncestorRec(rel.id, visited); } } } static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings) { if (willBeVisible(startY, startX, 3, 1)) { WINDOW* pad = newpad(3, 1); waddch(pad, ACS_VLINE); mvwaddch(pad, 1, 0, moreSiblings ? ACS_LTEE : ACS_VLINE); mvwaddch(pad, 2, 0, ACS_VLINE); drawpad(pad, startY, startX); delwin(pad); } return 3; } static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings) { assert(endX >= startX); const I16 len = endX - startX; if (willBeVisible(startY, startX, 2, len)) { WINDOW* pad = newpad(2, len); for(U16 i = 0; i < len-1; ++i) waddch(pad, ACS_HLINE); /* whline(pad, '_', len-1); */ waddch(pad, moreSiblings ? ACS_TTEE : ACS_URCORNER); mvwaddch(pad, 1, len-1, ACS_VLINE); drawpad(pad, startY, startX); delwin(pad); } return 2; } /* I16 Tree::drawLine(person_id id, I16 drawY, I16 drawX) const */ /* { */ /* people[id].draw(drawY, drawX, true, false, true); */ /* vector children; */ /* for(U32 i=0; i children; for(const auto& rel: rels) { if (rel.type == RelType::Parent) children.push_back(rel.id); else if (rel.type == RelType::Spouse && spouse == Nobody) spouse = rel.id; } const size_t numKids = children.size(); person.draw(drawY, drawX, id == selected_, spouse!=Nobody, false, numKids!=0); U16 spouse_width = 0; if (spouse != Nobody) { people[spouse].draw(drawY, drawX + person.boxWidth() + spouse_dist_, spouse == selected_, false, true); spouse_width = spouse_dist_ + people[spouse].boxWidth(); } auto childX = drawX; if (numKids!=0) { auto prevX = childX; drawY += BOX_HEIGHT; auto lnHt = linkToFirstBorn(drawY, childX, numKids > 1); /* Link height */ childX = drawLineWithWives(children[0], drawY + lnHt, childX); for(size_t j = 1; j < numKids; ++j) { lnHt = linkToChild(drawY+1, prevX+1, childX+1, j < numKids-1) + 1; /* +1 because LTC starts lower then LTFB */ prevX = childX; childX = drawLineWithWives(children[j], drawY + lnHt, childX); } } return max(drawX + person.boxWidth() + dist_between_ + spouse_width, childX); } void Tree::selectLeft() { if (findCommonAncestor(focused_, selected_) != Nobody) //if the selected is part of main tree { selectLeftSibSpouse(selected_); } else { person_id spouse = findSpouse(selected_); assert(spouse != Nobody); selected_ = spouse; } draw(); } void Tree::selectDown() { const auto& curRels = relations[selected_]; for(const Relation& rel: curRels) { if (rel.type == Parent) { selected_ = rel.id; draw(); return; } } } void Tree::selectUp() { person_id dad = Nobody, mom = Nobody; for(const Relation& rel: relations[selected_]) { if (rel.type == Child) { if (people[rel.id].isMale()) dad = rel.id; else mom = rel.id; } } if (mom == focused_ || (mom != Nobody && dad == Nobody)) selected_ = mom; else if (dad != Nobody) selected_ = dad; draw(); } void Tree::selectRight() { if (findCommonAncestor(focused_, selected_) != Nobody) //if the selected is part of main tree { if (person_id spouse = findSpouse(selected_); spouse != Nobody) { selected_ = spouse; } else { selectRightSib(selected_); } } else //if spouse of someone in main tree { person_id spouse = findSpouse(selected_); assert(spouse != Nobody); selectRightSib(spouse); } draw(); } void Tree::selectLeftSibSpouse(person_id person) /* todo: return a value and split */ { person_id leftSib = Nobody; if(findRelation(person, focused_) == Child) { auto siblings = findChildren(focused_); for(auto it = siblings.begin()+1; it < siblings.end(); ++it) { if(*it == person) { leftSib = *(it-1); break; } } } else if(person_id sel_par = findParent(person, true); sel_par != Nobody) { auto siblings = findChildren(sel_par); for(auto it = siblings.begin()+1; it < siblings.end(); ++it) { if(*it == person) { leftSib = *(it-1); break; } } } if(leftSib != Nobody) { person_id leftSibSpouse = findSpouse(leftSib); selected_ = leftSibSpouse != Nobody ? leftSibSpouse : leftSib; } } void Tree::selectRightSib(person_id person) { if(findRelation(person, focused_) == Child) { auto siblings = findChildren(focused_); for(auto it = siblings.begin(); it < siblings.end()-1; ++it) { if(*it == person) { selected_ = *(it+1); break; } } } else if(person_id sel_par = findParent(person, true); sel_par != Nobody) { auto siblings = findChildren(sel_par); for(auto it = siblings.begin(); it < siblings.end()-1; ++it) { if(*it == person) { selected_ = *(it+1); break; } } } }