594 lines
15 KiB
C++
594 lines
15 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <assert.h>
|
|
#include"tree.h"
|
|
#include"person.h"
|
|
#include"help.h"
|
|
|
|
using std::cout;
|
|
|
|
|
|
Tree Tree::operator+ (const Tree& other)//
|
|
{
|
|
Tree result(treeName + other.treeName); //The tree resulting from the addition
|
|
unsigned *newId = new unsigned[other.numPeople]; //The indexes members of the II tree will have in result
|
|
|
|
unsigned idCounter = numPeople;
|
|
for (unsigned i = 0, j; i < other.numPeople; ++i) //Searches for people present in both trees and gives them the same index in result
|
|
{
|
|
for (j = 0; j < numPeople; ++j)
|
|
{
|
|
if (other.people[i] == people[j])
|
|
{
|
|
newId[i] = j;
|
|
break;
|
|
}
|
|
}
|
|
if (j >= numPeople)
|
|
newId[i] = ++idCounter;
|
|
}
|
|
result.numPeople = idCounter; //The number of members the resulting tree has
|
|
|
|
result.people.resize(result.numPeople);
|
|
result.people = people; //Adds the members of the I tree to the resulting tree
|
|
for (unsigned i=0; i < other.numPeople; ++i) //Adds the members of the II tree to the resulting tree
|
|
{
|
|
if (newId[i] >= numPeople)
|
|
result.people.push(other.people[i]);
|
|
}
|
|
|
|
result.relations.resize(result.numPeople);
|
|
result.relations = relations;
|
|
|
|
for (unsigned i=0; i < other.numPeople; ++i) //Adds the relatives from the II tree
|
|
{
|
|
if (newId[i] >= numPeople) //If the member is found in the II tree but not in the I
|
|
{
|
|
result.relations.push(other.relations[i]);
|
|
}
|
|
else //If he's found in both
|
|
{
|
|
for (unsigned j = 0; j < other.people[i].numRel_; ++j) //Adds the number of relatives from the II to the number in the I
|
|
{
|
|
if(newId[other.relations[i][j].id] > numPeople)
|
|
++result.people[newId[i]].numRel_;
|
|
}
|
|
|
|
result.relations[newId[i]].resize(result.people[newId[i]].numRel_);
|
|
|
|
result.relations[newId[i]] = relations[newId[i]];
|
|
|
|
result.relations[newId[i]] += other.relations[i]; //The relatives from the II tree
|
|
}
|
|
}
|
|
|
|
delete[]newId;
|
|
return result;
|
|
}
|
|
|
|
Tree& Tree::operator+=(const Tree& other)
|
|
{
|
|
unsigned *newId = new unsigned[other.numPeople]; //The indexes people from II tree will have in I tree
|
|
|
|
unsigned idCounter = numPeople;
|
|
for (unsigned i = 0, j; i < other.numPeople; ++i) //Searches for people present in both trees and gives them the same index in I
|
|
{
|
|
for (j = 0; j < numPeople; ++j)
|
|
{
|
|
if (other.people[i] == people[j])
|
|
{
|
|
newId[i] = j;
|
|
break;
|
|
}
|
|
}
|
|
if (j >= numPeople)
|
|
newId[i] = idCounter++;
|
|
}
|
|
|
|
people.resize(idCounter);
|
|
for (unsigned i = 0; i < other.numPeople; ++i) //Adds the member personal data of II tree to I tree
|
|
{
|
|
if (newId[i] >= numPeople)
|
|
people.push(other.people[i]);
|
|
}
|
|
|
|
relations.resize(idCounter);
|
|
for (unsigned i = 0; i < other.numPeople; ++i) //Merges the relative data
|
|
{
|
|
if (newId[i] >= numPeople)
|
|
{
|
|
relations.push(other.relations[i]);
|
|
for (unsigned j = 0; j < other.people[i].numRel_; ++j)
|
|
relations[newId[i]][j].id = newId[ relations[newId[i]][j].id ];
|
|
}
|
|
else //If the member is present in both trees
|
|
{
|
|
for (unsigned j = 0; j < other.people[i].numRel_; ++j) //Adds the number of relatives from the II to the number in the I
|
|
{
|
|
if (newId[other.relations[i][j].id] >= numPeople)
|
|
++people[newId[i]].numRel_;
|
|
}
|
|
|
|
relations[newId[i]].resize(people[newId[i]].numRel_);
|
|
for (unsigned j = 0; j < other.people[i].numRel_; ++j)
|
|
{
|
|
relations[newId[i]][j + people[newId[i]].numRel_ - other.people[i].numRel_].id = newId[other.relations[i][j].id];
|
|
relations[newId[i]][j + people[newId[i]].numRel_ - other.people[i].numRel_].type = other.relations[i][j].type;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete[]newId;
|
|
numPeople = idCounter; //Number of members in I tree
|
|
return *this;
|
|
}
|
|
|
|
Tree& Tree::operator-= (const Tree& other)
|
|
{
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
{
|
|
for (unsigned j = 0; j < other.numPeople; ++j)
|
|
{
|
|
if (people[i] == other.people[j])
|
|
{
|
|
removePerson(i);
|
|
--i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
void Tree::rename(const char* newName)
|
|
{
|
|
treeName = newName;
|
|
}
|
|
|
|
unsigned Tree::findId(const char* name, const short year, const unsigned char month, const unsigned char day)const
|
|
{
|
|
//TODO
|
|
//Person sought(name, day, month, year,)
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
{
|
|
if (name == people[i].name_) //&& (year == people[i].year || !year || !people[i].year) && (month == people[i].month || !month || !people[i].month) && (day == people[i].day || !day || people[i].day))
|
|
return i;
|
|
}
|
|
return Nobody;
|
|
}
|
|
|
|
unsigned Tree::findOldestAncestor(unsigned id, bool isMale)const
|
|
{
|
|
for (unsigned i = 0; i < people[id].numRel_; ++i)
|
|
{
|
|
if (relations[id][i].type == Child && people[ relations[id][i].id ].isMale_ == isMale)
|
|
return findOldestAncestor(relations[id][i].id, isMale);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
unsigned Tree::findCommonAncestor(const unsigned firstId, const unsigned secondId)const
|
|
{
|
|
Array<char> visited(numPeople); //if a member is visited twice he's a common ancestor
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
visited[i] = 0;
|
|
commonAncestorRec(firstId, visited);
|
|
commonAncestorRec(secondId, visited);
|
|
|
|
unsigned oldestId = Nobody;
|
|
U16 oldestBday = SHRT_MAX;
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
{
|
|
if (visited[i] == 2 && people[i].birth_.year < oldestBday)
|
|
{
|
|
oldestBday = people[i].birth_.year;
|
|
oldestId = i;
|
|
}
|
|
}
|
|
return oldestId;
|
|
}
|
|
|
|
void Tree::saveTree() const
|
|
{
|
|
std::ofstream peopleFile(treeName+ ".people", std::ios::binary);
|
|
|
|
peopleFile.write(reinterpret_cast<const char*>(&numPeople), sizeof(numPeople));
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
people[i].save(peopleFile);
|
|
peopleFile.close();
|
|
|
|
std::ofstream relationFile(treeName + ".relations", std::ios::binary);
|
|
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
{
|
|
//relationFile.write((char*)&people[i].numRel_, sizeof(people[i].numRel_));
|
|
for (unsigned j = 0; j < people[i].numRel_; ++j)
|
|
relationFile.write(reinterpret_cast<char*>(&relations[i][j]), sizeof(relations[i][j]));
|
|
}
|
|
relationFile.close();
|
|
}
|
|
|
|
bool Tree::loadTree()
|
|
{
|
|
std::ifstream peopleFile(treeName + ".people", std::ios::binary);
|
|
if (peopleFile.peek() == std::char_traits<char>::eof())
|
|
{
|
|
peopleFile.close();
|
|
return 0;
|
|
}
|
|
|
|
peopleFile.read(reinterpret_cast<char*>(&numPeople), sizeof(numPeople));
|
|
people.resize(numPeople);
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
people[i].load(peopleFile);
|
|
people.sNum(numPeople);
|
|
peopleFile.close();
|
|
|
|
|
|
std::ifstream relationFile(treeName + ".relations", std::ios::binary);
|
|
relations.resize(numPeople);
|
|
for (unsigned i = 0; i < numPeople; ++i)
|
|
{
|
|
relations[i].resize(people[i].numRel_);
|
|
for (unsigned j = 0; j < people[i].numRel_; ++j)
|
|
relationFile.read(reinterpret_cast<char*>(&relations[i][j]), sizeof(relations[i][j]));
|
|
relations[i].sNum(people[i].numRel_);
|
|
}
|
|
relations.sNum(numPeople);
|
|
relationFile.close();
|
|
return 1;
|
|
}
|
|
|
|
void Tree::addPerson(const char* name, bool isMale, unsigned father, unsigned mother, EventTime birth, EventTime death)
|
|
{
|
|
Person newPerson(name, birth, isMale, death);
|
|
people.push(newPerson);
|
|
|
|
Array<Relation> newRels;
|
|
relations.push(newRels);
|
|
++numPeople;
|
|
|
|
addRelation(father, Parent, numPeople - 1);
|
|
addRelation(mother, Parent, numPeople - 1);
|
|
}
|
|
|
|
bool Tree::addRelation(const char* firstName, const RelType type, const char* secondName)
|
|
{
|
|
|
|
return addRelation(findId(firstName), type, findId(secondName));
|
|
}
|
|
|
|
bool Tree::addRelation(const unsigned firstId, const RelType type, const unsigned secondId, bool opposite)//
|
|
{
|
|
if (firstId == Nobody || firstId >= numPeople || secondId == Nobody || secondId >= numPeople || type == Invalid)
|
|
return 0;
|
|
|
|
unsigned i = 0;
|
|
for (; i < people[firstId].numRel_; ++i) //If they are already relatives
|
|
{
|
|
if (relations[firstId][i].id == secondId)
|
|
{
|
|
relations[firstId][i].type = type;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= people[firstId].numRel_)
|
|
{
|
|
relations[firstId].push({secondId, type});
|
|
++people[firstId].numRel_;
|
|
}
|
|
|
|
if (!opposite)
|
|
{
|
|
switch (type)
|
|
{
|
|
case Parent:
|
|
addRelation(secondId, Child, firstId, 1);
|
|
break;
|
|
case Child:
|
|
addRelation(secondId, Parent, firstId, 1);
|
|
break;
|
|
case Sibling:
|
|
case HalfSibling:
|
|
case Spouse:
|
|
case ExSpouse:
|
|
addRelation(secondId, type, firstId, 1);
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void Tree::removeRelation(const char* firstName, const char* secondName)
|
|
{
|
|
removeRelation(findId(firstName), findId(secondName));
|
|
}
|
|
|
|
void Tree::removePerson(const unsigned id)
|
|
{
|
|
unsigned relId; //Íoìåð íà òåêóùèÿ ðîäíèíàòà
|
|
for (unsigned i = 0; i < people[id].numRel_; ++i) //Ïðåìàõâàò ñå âðúçêèòå íà ÷ëåíà çà ïðåìàõâàíå
|
|
{
|
|
relId = relations[id][i].id;
|
|
for (unsigned j = 0; j < people[relId].numRel_; ++j)
|
|
{
|
|
if (relations[relId][j].id == id)
|
|
{
|
|
relations[relId].remove(j);
|
|
break;
|
|
}
|
|
}
|
|
--people[relId].numRel_;
|
|
}
|
|
for (unsigned i = 0; i < people[numPeople - 2].numRel_; ++i) //Ïîñëåäíèÿò ÷ëåí âçåìà íîìåðà íà ïðåìàõíàòèÿ
|
|
{
|
|
relId = relations[numPeople - 1][i].id;
|
|
for (unsigned j = 0; j < people[relId].numRel_; ++j)
|
|
{
|
|
if (relations[relId][j].id == numPeople - 1)
|
|
{
|
|
relations[relId][j].id = id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
relations.remove(id);
|
|
people.remove(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 firstId, const unsigned secondId)
|
|
{
|
|
for (unsigned i = 0; i < people[firstId].numRel_; ++i)
|
|
{
|
|
if (relations[firstId][i].id == secondId)
|
|
{
|
|
relations[firstId].remove(i);
|
|
--people[firstId].numRel_;
|
|
break;
|
|
}
|
|
}
|
|
for (unsigned i = 0; i < people[secondId].numRel_; ++i)
|
|
{
|
|
if (relations[secondId][i].id == firstId)
|
|
{
|
|
relations[secondId].remove(i);
|
|
--people[secondId].numRel_;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Tree::draw(person_id focused) const
|
|
{
|
|
erase(); //Clear the screen
|
|
wnoutrefresh(stdscr);
|
|
|
|
U16 drawY = 0/* (LINES) / 2 */;
|
|
U16 drawX = 0/* (COLS) / 2 */;
|
|
drawLineWithWives(focused, drawY, drawX);
|
|
|
|
doupdate();
|
|
|
|
char c;
|
|
do
|
|
{
|
|
c = std::tolower(getch());
|
|
|
|
switch(c)
|
|
{
|
|
default: break;
|
|
}
|
|
}
|
|
while(c != 'w');
|
|
}
|
|
|
|
void Tree::print() const
|
|
{
|
|
//for(const auto& member: people)
|
|
// member.print();
|
|
for(int i=0; i<numPeople;++i)
|
|
people[i].print();
|
|
}
|
|
|
|
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].print();
|
|
}
|
|
|
|
void Tree::printOldestAncestors(const unsigned id)const
|
|
{
|
|
people[findOldestAncestor(id)].print();
|
|
people[findOldestAncestor(id, 1)].print();
|
|
}
|
|
|
|
|
|
|
|
void Tree::commonAncestorRec(person_id id, const Array<char> &visited)const
|
|
{
|
|
for (unsigned i = 0; i < people[id].numRel_; ++i)
|
|
{
|
|
if (relations[id][i].type == Child)
|
|
{
|
|
++visited[relations[id][i].id];
|
|
commonAncestorRec(relations[id][i].id, visited);
|
|
}
|
|
}
|
|
}
|
|
|
|
static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
|
{
|
|
if(startY >= -2 && startX >= 0)
|
|
{
|
|
WINDOW* win = newwin(3, 1, startY, startX); /* TODO: pad */
|
|
|
|
waddch(win, ACS_VLINE);
|
|
mvwaddch(win, 1, 0,
|
|
moreSiblings ? ACS_LTEE : ACS_VLINE);
|
|
mvwaddch(win, 2, 0, ACS_VLINE);
|
|
|
|
wnoutrefresh(win);
|
|
delwin(win);
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
|
{
|
|
assert(endX >= startX);
|
|
|
|
if(startY >= -1 && endX >= 0)
|
|
{
|
|
const I16 len = endX - startX;
|
|
WINDOW* win = newwin(2, len, startY, startX);
|
|
|
|
for(U16 i = 0; i < len-1; ++i)
|
|
waddch(win, ACS_HLINE);
|
|
/* whline(win, '_', len-1); */
|
|
waddch(win, moreSiblings ? ACS_TTEE : ACS_URCORNER);
|
|
|
|
mvwaddch(win, 1, len-1, ACS_VLINE);
|
|
|
|
wnoutrefresh(win);
|
|
delwin(win);
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
|
|
/* I16 Tree::drawLine(person_id id, I16 drawY, I16 drawX) const */
|
|
/* { */
|
|
/* people[id].draw(drawY, drawX, true, false, true); */
|
|
|
|
/* std::vector<person_id> children; */
|
|
/* for(U32 i=0; i<relations[id].gNum(); ++i) */
|
|
/* { */
|
|
/* if(relations[id][i].type == RelType::Parent) */
|
|
/* children.push_back(relations[id][i].id); */
|
|
/* } */
|
|
|
|
/* if(!children.empty()) */
|
|
/* { */
|
|
/* auto prevX = drawX; */
|
|
/* drawY += BOX_HEIGHT; */
|
|
|
|
/* auto lnHt = linkToFirstBorn(drawY, drawX); /\* Link height *\/ */
|
|
/* drawX = drawLine(children[0], drawY + lnHt, drawX); */
|
|
|
|
/* for(size_t j = 1; j < children.size(); ++j) */
|
|
/* { */
|
|
/* lnHt = linkToChild(drawY+1, prevX+1, drawX+1); */
|
|
/* prevX = drawX; */
|
|
/* drawX = drawLine(children[j], drawY + lnHt, drawX); */
|
|
/* } */
|
|
/* return drawX; */
|
|
/* } */
|
|
|
|
/* return drawX + people[id].boxWidth() + dist_between; */
|
|
/* } */
|
|
|
|
I16 Tree::drawLineWithWives(person_id id, I16 drawY, const I16 drawX) const
|
|
{
|
|
const Person& person = people[id];
|
|
const auto& rels = relations[id];
|
|
|
|
person_id spouse = Nobody;
|
|
std::vector<person_id> children;
|
|
for(person_id i = 0; i < rels.gNum(); ++i)
|
|
{
|
|
if(rels[i].type == RelType::Parent)
|
|
children.push_back(rels[i].id);
|
|
|
|
else if(rels[i].type == RelType::Spouse && spouse == Nobody)
|
|
spouse = rels[i].id;
|
|
}
|
|
const size_t numKids = children.size();
|
|
person.draw(drawY, drawX, spouse!=Nobody, false, numKids!=0);
|
|
|
|
U16 spouse_width = 0;
|
|
if(spouse != Nobody)
|
|
{
|
|
people[spouse].draw(drawY, drawX + person.boxWidth() + spouse_dist, 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);
|
|
prevX = childX;
|
|
childX = drawLineWithWives(children[j], drawY + lnHt, childX);
|
|
}
|
|
}
|
|
|
|
return max(drawX + person.boxWidth() + dist_between + spouse_width,
|
|
childX);
|
|
}
|