Files
tft/tree.cpp
T
2023-01-20 23:12:38 +02:00

831 lines
19 KiB
C++

#include <iostream>
#include <fstream>
#include <assert.h>
#include"tree.h"
#include"person.h"
#include"help.h"
using std::cout;
using std::vector;
Tree Tree::operator+ (const Tree& other) const
{
/* Tree result(treeName + other.treeName); //The tree resulting from the addition */
/* vector<person_id> newId; //The indexes members of the II tree will have in result */
/* newId.reserve(other.numPeople); */
/* U32 idCounter = numPeople; */
/* for (U32 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.reserve(result.numPeople); */
/* result.people = people; //Adds the members of the I tree to the resulting tree */
/* for (U32 i=0; i < other.numPeople; ++i) //Adds the members of the II tree to the resulting tree */
/* { */
/* if (newId[i] >= numPeople) */
/* result.people.push_back(other.people[i]); */
/* } */
/* result.relations.reserve(result.numPeople); */
/* result.relations = relations; */
/* for (U32 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_back(other.relations[i]); */
/* } */
/* else //If he's found in both */
/* { */
/* for (person_id j = 0; j < other.relations[i].size(); ++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]].reserve(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 */
/* } */
/* } */
Tree result(*this);
return result += other;
}
Tree& Tree::operator+=(const Tree& other)
{
vector<person_id> 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.people[i].numRel_; ++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.people[i].numRel_; ++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
{
}
}
/* for (person_id 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; */
/* } */
}
}
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, const short year, const unsigned char month, const unsigned char day) const
{
//TODO
//Person sought(name, day, month, year,)
for (person_id 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;
}
person_id 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;
}
person_id Tree::findCommonAncestor(const unsigned firstId, const unsigned secondId) const
{
vector<U8> visited(numPeople); //if a member is visited twice he's a common ancestor
commonAncestorRec(firstId, visited);
commonAncestorRec(secondId, 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())
parent = rel.id;
}
return parent;
}
std::vector<person_id> Tree::findChildren(person_id person) const
{
vector<person_id> children;
for(const Relation& rel: relations[person])
{
if (rel.type == Parent)
children.push_back(rel.id);
}
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::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<const 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);
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]));
}
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_back(newPerson);
vector<Relation> newRels;
relations.push_back(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 == None)
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_back({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;
default: assert(!"Unexpected case"); break;
}
}
return 1;
}
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 < people[numPeople - 2].numRel_; ++i) //Move the last in his place and update IDs
{
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;
}
}
}
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 firstId, const unsigned secondId)
{
for (unsigned i = 0; i < relations[firstId].size(); ++i)
{
if (relations[firstId][i].id == secondId)
{
remove(relations[firstId], i);
break;
}
}
for (unsigned i = 0; i < relations[secondId].size(); ++i)
{
if (relations[secondId][i].id == firstId)
{
remove(relations[secondId], i);
break;
}
}
}
void Tree::display()
{
draw();
I32 c;
do
{
c = std::toupper(getch());
switch(c)
{
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::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<U8> &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); */
/* std::vector<person_id> children; */
/* for(U32 i=0; i<relations[id].size(); ++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(const 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(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;
}
}
}
}