MeOS version 3.6.1109

This commit is contained in:
Erik Melin 2019-10-08 21:58:19 +02:00
parent 06a6c4ff67
commit 7a99f0a4cd
15 changed files with 425 additions and 318 deletions

View File

@ -900,6 +900,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
wstring firstStart = gdi.getText("FirstStart");
wstring minInterval = gdi.getText("MinInterval");
wstring vacances = gdi.getText("Vacances");
setDefaultVacant(vacances);
clearPage(gdi, false);
gdi.addString("", boldLarge, "Lotta flera klasser");
@ -1044,13 +1045,14 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
gdi.addInput("FirstStart", oe->getAbsTime(3600), 10, 0, L"Första (ordinarie) start:");
gdi.addInput("MinInterval", L"2:00", 10, 0, L"Minsta startintervall:");
gdi.fillDown();
gdi.addInput("Vacances", L"5%", 10, 0, L"Andel vakanser:");
gdi.addInput("Vacances", getDefaultVacant(), 10, 0, L"Andel vakanser:");
gdi.popX();
createDrawMethod(gdi);
gdi.fillDown();
gdi.addCheckbox("LateBefore", "Efteranmälda före ordinarie");
gdi.addCheckbox("AllowNeighbours", "Tillåt samma bana inom basintervall", 0, oe->getPropertyInt("DrawInterlace", 1) != 0);
gdi.dropLine();
gdi.popX();
@ -1180,8 +1182,11 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
return 0;
wstring minInterval = gdi.getText("MinInterval");
wstring vacances = gdi.getText("Vacances");
bool lateBefore = false;
//bool pairwise = gdi.isChecked("Pairwise");
setDefaultVacant(vacances);
bool lateBefore = gdi.isChecked("LateBefore");
bool allowNeighbourSameCourse = gdi.isChecked("AllowNeighbours");
oe->setProperty("DrawInterlace", allowNeighbourSameCourse ? 1 : 0);
int pairSize = 1;
if (gdi.hasField("PairSize")) {
pairSize = gdi.getSelectedItem("PairSize").first;
@ -1201,7 +1206,7 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
clearPage(gdi, true);
oe->automaticDrawAll(gdi, firstStart, minInterval, vacances,
lateBefore, method, pairSize);
lateBefore, allowNeighbourSameCourse, method, pairSize);
oe->addAutoBib();
gdi.scrollToBottom();
gdi.addButton("Cancel", "Återgå", ClassesCB);
@ -1263,10 +1268,19 @@ int TabClass::classCB(gdioutput &gdi, int type, void *data)
int origin = bi.getExtraInt();
wstring firstStart = oe->getAbsTime(3600);
wstring minInterval = L"2:00";
wstring vacances = L"5%";
wstring vacances = getDefaultVacant();
if (gdi.hasField("Vacances")) {
vacances = gdi.getText("Vacances");
setDefaultVacant(vacances);
}
int maxNumControl = 1;
int pairSize = 1;
if (gdi.hasField("AllowNeighbours")) {
bool allowNeighbourSameCourse = gdi.isChecked("AllowNeighbours");
oe->setProperty("DrawInterlace", allowNeighbourSameCourse ? 1 : 0);
}
//bool pairwise = false;
int by = 0;
int bx = gdi.getCX();
@ -4387,6 +4401,7 @@ void TabClass::readDrawInfo(gdioutput &gdi, DrawInfo &drawInfoOut) {
int maxVacancy = gdi.getTextNo("VacancesMax");
int minVacancy = gdi.getTextNo("VacancesMin");
setDefaultVacant(gdi.getText("Vacances"));
double vacancyFactor = 0.01*_wtof(gdi.getText("Vacances").c_str());
double extraFactor = 0.01*_wtof(gdi.getText("Extra").c_str());
@ -4661,7 +4676,7 @@ void TabClass::loadBasicDrawSetup(gdioutput &gdi, int &bx, int &by, const wstrin
gdi.popX();
gdi.dropLine(4);
gdi.fillDown();
gdi.addCheckbox("AllowNeighbours", "Tillåt samma bana inom basintervall", 0, oe->getPropertyInt("DrawInterlace", 1) == 0);
gdi.addCheckbox("AllowNeighbours", "Tillåt samma bana inom basintervall", 0, oe->getPropertyInt("DrawInterlace", 1) != 0);
gdi.addCheckbox("CoursesTogether", "Lotta klasser med samma bana gemensamt", 0, false);
gdi.dropLine(0.5);
@ -4680,8 +4695,10 @@ void TabClass::loadBasicDrawSetup(gdioutput &gdi, int &bx, int &by, const wstrin
gdi.fillRight();
gdi.popX();
gdi.addInput("Vacances", vacances, 6, 0, L"Andel vakanser:");
gdi.addInput("VacancesMin", L"1", 6, 0, L"Min. vakanser (per klass):");
gdi.addInput("VacancesMax", L"10", 6, 0, L"Max. vakanser (per klass):");
bool zeroVac = _wtoi(vacances.c_str()) == 0;
gdi.addInput("VacancesMin", zeroVac ? L"0" : L"1", 6, 0, L"Min. vakanser (per klass):");
gdi.addInput("VacancesMax", zeroVac ? L"0" : L"10", 6, 0, L"Max. vakanser (per klass):");
gdi.addInput("Extra", L"0%", 6, 0, L"Förväntad andel efteranmälda:");
gdi.dropLine(4);
@ -4749,3 +4766,17 @@ void TabClass::loadReadyToDistribute(gdioutput &gdi, int &bx, int &by) {
gdi.refresh();
}
wstring TabClass::getDefaultVacant() {
int dvac = oe->getPropertyInt("VacantPercent", -1);
if (dvac >= 0 && dvac <= 100)
return itow(dvac) + L" %";
else
return L"5 %";
}
void TabClass::setDefaultVacant(const wstring &v) {
int val = _wtoi(v.c_str());
if (val >= 0 && val <= 100)
oe->setProperty("VacantPercent", val);
}

View File

@ -156,6 +156,9 @@ class TabClass :
int maxNumControl, const wstring& minInterval, const wstring& vacances, const set<int> &clsId);
void loadReadyToDistribute(gdioutput &gdi, int &bx, int &by);
wstring getDefaultVacant();
void setDefaultVacant(const wstring &val);
public:
void clearCompetitionData();

View File

@ -1545,7 +1545,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
switch (startType) {
case SMCommon:
oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"0", L"0", false, oEvent::DrawMethod::Random, 1);
oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"0", L"0", false, false, oEvent::DrawMethod::Random, 1);
drawn = true;
break;
@ -1570,7 +1570,7 @@ int TabCompetition::competitionCB(gdioutput &gdi, int type, void *data)
}
}
if (!skip)
oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"2:00", L"2", true, oEvent::DrawMethod::MeOS, 1);
oe->automaticDrawAll(gdi, formatTimeHMS(firstStart), L"2:00", L"2", true, false, oEvent::DrawMethod::MeOS, 1);
drawn = true;
break;
}

View File

@ -1686,7 +1686,7 @@ bool Table::inputChange(gdioutput &gdi, HWND hdt)
int Table::getColumn(int x, bool limit) const
{
if (columns.empty())
if (columns.empty() || xpos.empty())
return -1;
if (x<xpos[0]) {

View File

@ -840,7 +840,7 @@ SI X är redan inläst. Ska den läsas in igen? = SI X is already read out. Read
SI på = SI on
SI-dubbletter: %d = SI duplicates: %d
SOFT-avgift = SOFT fee
SOFT-lottning = Swedish draw method
SOFT-lottning = Swedish legacy draw method
Saknad starttid = Missing Start Time
Sammanställning = Summary
Sammanställning, ekonomi = Summary, economy

View File

@ -179,8 +179,7 @@ void gdioutput::constructor(double _scale)
void gdioutput::setFont(int size, const wstring &font)
{
//setEncoding(enc);
double s = 1 + double(size)*0.25;// 1 + size * sqrt(double(size))*0.2;
double s = 1 + double(size)*0.25;
initCommon(s, font);
}

View File

@ -30,7 +30,7 @@
//V35: abcdef
//V36: abcdef
int getMeosBuild() {
string revision("$Rev: 915 $");
string revision("$Rev: 935 $");
return 174 + atoi(revision.substr(5, string::npos).c_str());
}
@ -42,12 +42,12 @@ int getMeosBuild() {
//V33: abcdefghij
//V34: abcdfge
wstring getMeosDate() {
wstring date(L"$Date: 2019-07-30 07:05:51 +0200 (ti, 30 jul 2019) $");
wstring date(L"$Date: 2019-10-05 21:53:12 +0200 (lö, 05 okt 2019) $");
return date.substr(7,10);
}
wstring getBuildType() {
return L"Update 2"; // No parantheses (...)
return L"Update 3"; // No parantheses (...)
}
wstring getMajorVersion() {
@ -135,6 +135,13 @@ void getSupporters(vector<wstring> &supp, vector<wstring> &developSupp)
supp.emplace_back(L"JWOC 2019");
developSupp.emplace_back(L"OK Nackhe");
supp.emplace_back(L"OK Rodhen");
supp.emplace_back(L"HEYRIES");
developSupp.emplace_back(L"SongTao Wang / Henan Zhixing Exploration Sports Culture Co., Ltd.");
developSupp.emplace_back(L"Australian and Oceania Orienteering Championships 2019");
supp.emplace_back(L"Järfälla OK");
supp.emplace_back(L"TJ Slávia Farmaceut Bratislava");
supp.emplace_back(L"OK Tyr, Karlstad");
supp.emplace_back(L"Magnus Thornell, Surahammars SOK");
reverse(supp.begin(), supp.end());
}

View File

@ -71,9 +71,7 @@ protected:
mutable int tMapsUsed = -1;
mutable int tMapsUsedNoVacant = -1;
// Get an identity sum based on controls
int getIdSum(int nControls);
/// Add an control without update
pControl doAddControl(int Id);
@ -95,6 +93,9 @@ protected:
public:
// Get an identity sum based on controls
int getIdSum(int nControls);
void getClasses(vector<pClass> &usageClass) const;
void remove();

View File

@ -799,10 +799,12 @@ bool oDataContainer::isModified(const oDataInfo &di,
return memcmp(vd, vdOld, 8) != 0;
}
}
else if (di.Type == oDTString){
char * vd=(char *)(data)+di.Index;
char * vdOld=(char *)(oldData)+di.Index;
return strcmp(vd, vdOld) != 0;
else if (di.Type == oDTString) {
LPBYTE vdB = LPBYTE(data) + di.Index;
LPBYTE vdOldB = LPBYTE(oldData) + di.Index;
wchar_t * vd=(wchar_t *)vdB;
wchar_t * vdOld=(wchar_t *)vdOldB;
return wcscmp(vd, vdOld) != 0;
}
else if (di.Type == oDTStringDynamic){
const wstring &newS = (*strptr)[0][di.Index];

View File

@ -2604,7 +2604,6 @@ int oEvent::getRelativeTime(const string &date, const string &absoluteTime, cons
else return -1;
}
int oEvent::getRelativeTime(const wstring &m) const {
int dayIndex = 0;
for (size_t k = 0; k + 1 < m.length(); k++) {
@ -2947,12 +2946,13 @@ void oEvent::generateVacancyList(gdioutput &gdi, GUICALLBACK cb)
oRunnerList::iterator it;
// BIB, START, NAME, CLUB, SI
int dx[5]={0, 0, 70, 150};
int dx[5]={0, 0, gdi.scaleLength(70), gdi.scaleLength(150)};
bool withbib=hasBib(true, false);
int i;
if (withbib) for (i=1;i<4;i++) dx[i]+=40;
const int bibLen = gdi.scaleLength(40);
if (withbib) for (i = 1; i < 4; i++) dx[i] += bibLen;
int y=gdi.getCY();
int x=gdi.getCX();
@ -2984,7 +2984,7 @@ void oEvent::generateVacancyList(gdioutput &gdi, GUICALLBACK cb)
if (nRunner>=RunnersPerCol) {
y = yStart;
x += dx[3]+5;
x += dx[3]+gdi.scaleLength(5);
nRunner = 0;
}

View File

@ -588,7 +588,7 @@ public:
// Automatic draw of all classes
void automaticDrawAll(gdioutput &gdi, const wstring &firstStart,
const wstring &minIntervall, const wstring &vacances,
bool lateBefore, DrawMethod method, int pairSize);
bool lateBefore, bool allowNeighbourSameCourse, DrawMethod method, int pairSize);
// Restore a backup by renamning the file to .meos
void restoreBackup();
@ -773,10 +773,6 @@ public:
/** Use the current computer time to convert the specified time to a long time, if long times are used. */
int convertToFullTime(int inTime);
/** Internal version of start order optimizer */
void optimizeStartOrder(vector< vector<pair<int, int> > > &StartField, DrawInfo &drawInfo,
vector<ClassInfo> &cInfo, int useNControls, int alteration);
struct ResultEvent {
ResultEvent() {}
ResultEvent(pRunner r, int time, int control, RunnerStatus status):

View File

@ -421,6 +421,328 @@ namespace {
}
}
class DrawOptimAlgo {
private:
oEvent * oe;
vector<pClass> Classes;
vector<pRunner> Runners;
int maxNRunner = 0;
int maxGroup = 0;
int maxCourse = 0;
int bestEndPos = 0;
map<int, ClassInfo> otherClasses;
int bestEndPosGlobal = 0;
static int optimalLayout(int interval, vector< pair<int, int> > &classes) {
sort(classes.begin(), classes.end());
vector<int> chaining(interval, 0);
for (int k = int(classes.size()) - 1; k >= 0; k--) {
int ix = 0;
// Find free position
for (int i = 1; i<interval; i++) {
if (chaining[i] < chaining[ix])
ix = i;
}
int nr = classes[k].first;
if (chaining[ix] > 0)
nr += classes[k].second;
chaining[ix] += 1 + interval * (nr - 1);
}
int last = chaining[0];
for (int i = 1; i<interval; i++) {
last = max(chaining[i], last);
}
return last;
}
void computeBestStartDepth(DrawInfo &di, vector<ClassInfo> &cInfo, int useNControls, int alteration) {
//OutputDebugString((L"Use NC:" + itow(useNControls)).c_str());
if (di.firstStart <= 0)
di.firstStart = 0;
otherClasses.clear();
cInfo.clear();
map<int, int> runnerPerGroup;
map<int, int> runnerPerCourse;
int nRunnersTot = 0;
for (auto c_it : Classes) {
bool drawClass = di.classes.count(c_it->getId()) > 0;
ClassInfo *cPtr = 0;
if (!drawClass) {
otherClasses[c_it->getId()] = ClassInfo(&*c_it);
cPtr = &otherClasses[c_it->getId()];
}
else
cPtr = &di.classes[c_it->getId()];
ClassInfo &ci = *cPtr;
pCourse pc = c_it->getCourse();
if (pc && useNControls < 1000) {
if (useNControls > 0 && pc->getNumControls() > 0)
ci.unique = 1000000 + pc->getIdSum(useNControls);
else
ci.unique = 10000 + pc->getId();
ci.courseId = pc->getId();
}
else
ci.unique = ci.classId;
if (!drawClass)
continue;
int nr = c_it->getNumRunners(true, true, true);
if (ci.nVacant == -1 || !ci.nVacantSpecified || di.changedVacancyInfo) {
// Auto initialize
int nVacancies = int(nr * di.vacancyFactor + 0.5);
nVacancies = max(nVacancies, di.minVacancy);
nVacancies = min(nVacancies, di.maxVacancy);
nVacancies = max(nVacancies, 0);
if (di.vacancyFactor == 0)
nVacancies = 0;
ci.nVacant = nVacancies;
ci.nVacantSpecified = false;
}
if (!ci.nExtraSpecified || di.changedExtraInfo) {
// Auto initialize
ci.nExtra = max(int(nr * di.extraFactor + 0.5), 1);
if (di.extraFactor == 0)
ci.nExtra = 0;
ci.nExtraSpecified = false;
}
ci.nRunners = nr + ci.nVacant;
if (ci.nRunners > 0) {
nRunnersTot += ci.nRunners + ci.nExtra;
cInfo.push_back(ci);
runnerPerGroup[ci.unique] += ci.nRunners + ci.nExtra;
runnerPerCourse[ci.courseId] += ci.nRunners + ci.nExtra;
}
}
maxGroup = 0;
maxCourse = 0;
maxNRunner = 0;
int a = 1 + (alteration % 7);
int b = (alteration % 3);
int c = alteration % 5;
for (size_t k = 0; k < cInfo.size(); k++) {
maxNRunner = max(maxNRunner, cInfo[k].nRunners);
cInfo[k].nRunnersGroup = runnerPerGroup[cInfo[k].unique];
cInfo[k].nRunnersCourse = runnerPerCourse[cInfo[k].courseId];
maxGroup = max(maxGroup, cInfo[k].nRunnersGroup);
maxCourse = max(maxCourse, cInfo[k].nRunnersCourse);
cInfo[k].sortFactor = cInfo[k].nRunners * a + cInfo[k].nRunnersGroup * b + cInfo[k].nRunnersCourse * c;
}
/*for (size_t k = 0; k < cInfo.size(); k++) {
auto pc = oe->getClass(cInfo[k].classId);
auto c = pc->getCourse();
wstring cc;
for (int i = 0; i < 3; i++)
cc += itow(c->getControl(i)->getId()) + L",";
wstring w = pc->getName() + L"; " + cc + L"; " + itow(cInfo[k].unique) + L"; " + itow(cInfo[k].nRunners) + L"\n";
OutputDebugString(w.c_str());
}*/
di.numDistinctInit = runnerPerGroup.size();
di.numRunnerSameInitMax = maxGroup;
di.numRunnerSameCourseMax = maxCourse;
// Calculate the theoretical best end position to use.
bestEndPos = 0;
for (map<int, int>::iterator it = runnerPerGroup.begin(); it != runnerPerGroup.end(); ++it) {
vector< pair<int, int> > classes;
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].unique == it->first)
classes.push_back(make_pair(cInfo[k].nRunners, cInfo[k].nExtra));
}
int optTime = optimalLayout(di.minClassInterval / di.baseInterval, classes);
bestEndPos = max(optTime, bestEndPos);
}
if (nRunnersTot > 0)
bestEndPos = max(bestEndPos, nRunnersTot / di.nFields);
if (!di.allowNeighbourSameCourse)
bestEndPos = max(bestEndPos, maxCourse * 2 - 1);
else
bestEndPos = max(bestEndPos, maxCourse);
}
public:
DrawOptimAlgo(oEvent *oe) : oe(oe) {
oe->getClasses(Classes, false);
oe->getRunners(-1, -1, Runners);
}
void optimizeStartOrder(vector< vector<pair<int, int> > > &StartField, DrawInfo &di,
vector<ClassInfo> &cInfo, int useNControls, int alteration) {
if (bestEndPosGlobal == 0) {
bestEndPosGlobal = 100000;
for (int i = 1; i <= di.maxCommonControl && i<=10; i++) {
int ncc = i;
if (i >= 10)
ncc = 0;
computeBestStartDepth(di, cInfo, ncc, 0);
bestEndPosGlobal = min(bestEndPos, bestEndPosGlobal);
}
di.minimalStartDepth = bestEndPosGlobal * di.baseInterval;
}
computeBestStartDepth(di, cInfo, useNControls, alteration);
ClassInfo::sSortOrder = 0;
sort(cInfo.begin(), cInfo.end());
int maxSize = di.minClassInterval * maxNRunner;
// Special case for constant time start
if (di.baseInterval == 0) {
di.baseInterval = 1;
di.minClassInterval = 0;
}
// Calculate an estimated maximal class intervall
for (size_t k = 0; k < cInfo.size(); k++) {
int quotient = maxSize / (cInfo[k].nRunners*di.baseInterval);
if (quotient*di.baseInterval > di.maxClassInterval)
quotient = di.maxClassInterval / di.baseInterval;
if (cInfo[k].nRunnersGroup >= maxGroup)
quotient = di.minClassInterval / di.baseInterval;
if (!cInfo[k].hasFixedTime)
cInfo[k].interval = quotient;
}
for (int m = 0; m < di.nFields; m++)
StartField[m].resize(3000);
int alternator = 0;
// Fill up with non-drawn classes
for (auto &it : Runners) {
int st = it->getStartTime();
int relSt = st - di.firstStart;
int relPos = relSt / di.baseInterval;
if (st > 0 && relSt >= 0 && relPos < 3000 && (relSt%di.baseInterval) == 0) {
if (otherClasses.count(it->getClassId(false)) == 0)
continue;
pClass cls = it->getClassRef(true);
if (cls) {
if (!di.startName.empty() && cls->getStart() != di.startName)
continue;
if (cls->hasFreeStart())
continue;
}
ClassInfo &ci = otherClasses[it->getClassId(false)];
int k = 0;
while (true) {
if (k == StartField.size()) {
StartField.push_back(vector< pair<int, int> >());
StartField.back().resize(3000);
}
if (StartField[k][relPos].first == 0) {
StartField[k][relPos].first = ci.unique;
StartField[k][relPos].second = ci.courseId;
break;
}
k++;
}
}
}
// Fill up classes with fixed starttime
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].hasFixedTime) {
insertStart(StartField, di.nFields, cInfo[k]);
}
}
if (di.minClassInterval == 0) {
// Set fixed start time
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].hasFixedTime)
continue;
cInfo[k].firstStart = di.firstStart;
cInfo[k].interval = 0;
}
}
else {
// Do the distribution
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].hasFixedTime)
continue;
int minPos = 1000000;
int minEndPos = 1000000;
int minInterval = cInfo[k].interval;
for (int i = di.minClassInterval / di.baseInterval; i <= cInfo[k].interval; i++) {
int startpos = alternator % max(1, (bestEndPos - cInfo[k].nRunners * i) / 3);
startpos = 0;
int ipos = startpos;
int t = 0;
while (!isFree(di, StartField, di.nFields, ipos, i, cInfo[k])) {
t++;
// Algorithm to randomize start position
// First startpos -> bestEndTime, then 0 -> startpos, then remaining
if (t < (bestEndPos - startpos))
ipos = startpos + t;
else {
ipos = t - (bestEndPos - startpos);
if (ipos >= startpos)
ipos = t;
}
}
int endPos = ipos + i * cInfo[k].nRunners;
if (endPos < minEndPos || endPos < bestEndPos) {
minEndPos = endPos;
minPos = ipos;
minInterval = i;
}
}
cInfo[k].firstStart = minPos;
cInfo[k].interval = minInterval;
cInfo[k].overShoot = max(minEndPos - bestEndPosGlobal, 0);
insertStart(StartField, di.nFields, cInfo[k]);
alternator += alteration;
}
}
}
};
void oEvent::optimizeStartOrder(gdioutput &gdi, DrawInfo &di, vector<ClassInfo> &cInfo)
{
if (Classes.size()==0)
@ -439,12 +761,15 @@ void oEvent::optimizeStartOrder(gdioutput &gdi, DrawInfo &di, vector<ClassInfo>
int nCtrl = 1;//max(1, di.maxCommonControl-2);
const int maxControlDiff = di.maxCommonControl < 1000 ? di.maxCommonControl : 10;
bool checkOnlyClass = di.maxCommonControl == 1000;
DrawOptimAlgo drawOptim(this);
while (!found) {
StartParam optInner;
for (int alt = 0; alt <= 20 && !found; alt++) {
vector< vector<pair<int, int> > > startField(di.nFields);
optimizeStartOrder(startField, di, cInfo, nCtrl, alt);
drawOptim.optimizeStartOrder(startField, di, cInfo, nCtrl, alt);
int overShoot = 0;
int overSum = 0;
@ -495,7 +820,7 @@ void oEvent::optimizeStartOrder(gdioutput &gdi, DrawInfo &di, vector<ClassInfo>
}
vector< vector<pair<int, int> > > startField(di.nFields);
optimizeStartOrder(startField, di, cInfo, opt.nControls, opt.alternator);
drawOptim.optimizeStartOrder(startField, di, cInfo, opt.nControls, opt.alternator);
gdi.addString("", 0, "Identifierar X unika inledningar på banorna.#" + itos(di.numDistinctInit));
gdi.addString("", 0, "Största gruppen med samma inledning har X platser.#" + itos(di.numRunnerSameInitMax));
@ -545,33 +870,6 @@ void oEvent::optimizeStartOrder(gdioutput &gdi, DrawInfo &di, vector<ClassInfo>
gdi.dropLine();
}
int optimalLayout(int interval, vector< pair<int, int> > &classes) {
sort(classes.begin(), classes.end());
vector<int> chaining(interval, 0);
for (int k = int(classes.size())-1 ; k >= 0; k--) {
int ix = 0;
// Find free position
for (int i = 1; i<interval; i++) {
if (chaining[i] < chaining[ix])
ix = i;
}
int nr = classes[k].first;
if (chaining[ix] > 0)
nr += classes[k].second;
chaining[ix] += 1 + interval*(nr-1);
}
int last = chaining[0];
for (int i = 1; i<interval; i++) {
last = max(chaining[i], last);
}
return last;
}
void oEvent::loadDrawSettings(const set<int> &classes, DrawInfo &drawInfo, vector<ClassInfo> &cInfo) const {
drawInfo.firstStart = 3600 * 22;
drawInfo.minClassInterval = 3600;
@ -656,250 +954,6 @@ void oEvent::loadDrawSettings(const set<int> &classes, DrawInfo &drawInfo, vecto
cInfo[k].nRunnersCourse = runnerPerCourse[cInfo[k].courseId];
}
}
void oEvent::optimizeStartOrder(vector< vector<pair<int, int> > > &StartField, DrawInfo &di,
vector<ClassInfo> &cInfo, int useNControls, int alteration)
{
if (di.firstStart<=0)
di.firstStart = 0;
if (di.minClassInterval < di.baseInterval) {
throw meosException("Startintervallet får inte vara kortare än basintervallet.");
}
map<int, ClassInfo> otherClasses;
cInfo.clear();
oClassList::iterator c_it;
map<int, int> runnerPerGroup;
map<int, int> runnerPerCourse;
int nRunnersTot = 0;
for (c_it=Classes.begin(); c_it != Classes.end(); ++c_it) {
bool drawClass = di.classes.count(c_it->getId())>0;
ClassInfo *cPtr = 0;
if (!drawClass) {
otherClasses[c_it->getId()] = ClassInfo(&*c_it);
cPtr = &otherClasses[c_it->getId()];
}
else
cPtr = &di.classes[c_it->getId()];
ClassInfo &ci = *cPtr;
pCourse pc = c_it->getCourse();
if (pc && useNControls < 1000) {
if (useNControls>0 && pc->nControls>0)
ci.unique = 1000000 + pc->getIdSum(useNControls);
else
ci.unique = 10000 + pc->getId();
ci.courseId = pc->getId();
}
else
ci.unique = ci.classId;
if (!drawClass)
continue;
int nr = c_it->getNumRunners(true, true, true);
if (ci.nVacant == -1 || !ci.nVacantSpecified || di.changedVacancyInfo) {
// Auto initialize
int nVacancies = int(nr * di.vacancyFactor + 0.5);
nVacancies = max(nVacancies, di.minVacancy);
nVacancies = min(nVacancies, di.maxVacancy);
nVacancies = max(nVacancies, 0);
if (di.vacancyFactor == 0)
nVacancies = 0;
ci.nVacant = nVacancies;
ci.nVacantSpecified = false;
}
if (!ci.nExtraSpecified || di.changedExtraInfo) {
// Auto initialize
ci.nExtra = max(int(nr * di.extraFactor + 0.5), 1);
if (di.extraFactor == 0)
ci.nExtra = 0;
ci.nExtraSpecified = false;
}
ci.nRunners = nr + ci.nVacant;
if (ci.nRunners>0) {
nRunnersTot += ci.nRunners + ci.nExtra;
cInfo.push_back(ci);
runnerPerGroup[ci.unique] += ci.nRunners + ci.nExtra;
runnerPerCourse[ci.courseId] += ci.nRunners + ci.nExtra;
}
}
int maxGroup = 0;
int maxCourse = 0;
int maxNRunner = 0;
int a = 1 + (alteration % 7);
int b = (alteration % 3);
int c = alteration % 5;
for (size_t k = 0; k<cInfo.size(); k++) {
maxNRunner = max(maxNRunner, cInfo[k].nRunners);
cInfo[k].nRunnersGroup = runnerPerGroup[cInfo[k].unique];
cInfo[k].nRunnersCourse = runnerPerCourse[cInfo[k].courseId];
maxGroup = max(maxGroup, cInfo[k].nRunnersGroup);
maxCourse = max(maxCourse, cInfo[k].nRunnersCourse);
cInfo[k].sortFactor = cInfo[k].nRunners * a + cInfo[k].nRunnersGroup * b + cInfo[k].nRunnersCourse * c;
}
di.numDistinctInit = runnerPerGroup.size();
di.numRunnerSameInitMax = maxGroup;
di.numRunnerSameCourseMax = maxCourse;
// Calculate the theoretical best end position to use.
int bestEndPos = 0;
for (map<int, int>::iterator it = runnerPerGroup.begin(); it != runnerPerGroup.end(); ++it) {
vector< pair<int, int> > classes;
for (size_t k = 0; k<cInfo.size(); k++) {
if (cInfo[k].unique == it->first)
classes.push_back(make_pair(cInfo[k].nRunners, cInfo[k].nExtra));
}
int optTime = optimalLayout(di.minClassInterval/di.baseInterval, classes);
bestEndPos = max(optTime, bestEndPos);
}
if (nRunnersTot > 0)
bestEndPos = max(bestEndPos, nRunnersTot / di.nFields);
bestEndPos = max(bestEndPos, maxCourse * 2);
di.minimalStartDepth = bestEndPos * di.baseInterval;
ClassInfo::sSortOrder = 0;
sort(cInfo.begin(), cInfo.end());
int maxSize = di.minClassInterval * maxNRunner;
// Special case for constant time start
if (di.baseInterval==0) {
di.baseInterval = 1;
di.minClassInterval = 0;
}
// Calculate an estimated maximal class intervall
for (size_t k = 0; k < cInfo.size(); k++) {
int quotient = maxSize/(cInfo[k].nRunners*di.baseInterval);
if (quotient*di.baseInterval > di.maxClassInterval)
quotient=di.maxClassInterval/di.baseInterval;
if (cInfo[k].nRunnersGroup >= maxGroup)
quotient = di.minClassInterval / di.baseInterval;
if (!cInfo[k].hasFixedTime)
cInfo[k].interval = quotient;
}
for(int m=0;m < di.nFields;m++)
StartField[m].resize(3000);
int alternator = 0;
// Fill up with non-drawn classes
for (oRunnerList::iterator it = Runners.begin(); it!=Runners.end(); ++it) {
int st = it->getStartTime();
int relSt = st-di.firstStart;
int relPos = relSt / di.baseInterval;
if (st>0 && relSt>=0 && relPos<3000 && (relSt%di.baseInterval) == 0) {
if (otherClasses.count(it->getClassId(false))==0)
continue;
if (!di.startName.empty() && it->Class && it->Class->getStart()!=di.startName)
continue;
ClassInfo &ci = otherClasses[it->getClassId(false)];
int k = 0;
while(true) {
if (k==StartField.size()) {
StartField.push_back(vector< pair<int, int> >());
StartField.back().resize(3000);
}
if (StartField[k][relPos].first==0) {
StartField[k][relPos].first = ci.unique;
StartField[k][relPos].second = ci.courseId;
break;
}
k++;
}
}
}
// Fill up classes with fixed starttime
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].hasFixedTime) {
insertStart(StartField, di.nFields, cInfo[k]);
}
}
if (di.minClassInterval == 0) {
// Set fixed start time
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].hasFixedTime)
continue;
cInfo[k].firstStart = di.firstStart;
cInfo[k].interval = 0;
}
}
else {
// Do the distribution
for (size_t k = 0; k < cInfo.size(); k++) {
if (cInfo[k].hasFixedTime)
continue;
int minPos = 1000000;
int minEndPos = 1000000;
int minInterval=cInfo[k].interval;
for (int i = di.minClassInterval/di.baseInterval; i<=cInfo[k].interval; i++) {
int startpos = alternator % max(1, (bestEndPos - cInfo[k].nRunners * i)/3);
startpos = 0;
int ipos = startpos;
int t = 0;
while( !isFree(di, StartField, di.nFields, ipos, i, cInfo[k]) ) {
t++;
// Algorithm to randomize start position
// First startpos -> bestEndTime, then 0 -> startpos, then remaining
if (t<(bestEndPos-startpos))
ipos = startpos + t;
else {
ipos = t - (bestEndPos-startpos);
if (ipos>=startpos)
ipos = t;
}
}
int endPos = ipos + i*cInfo[k].nRunners;
if (endPos < minEndPos || endPos < bestEndPos) {
minEndPos = endPos;
minPos = ipos;
minInterval = i;
}
}
cInfo[k].firstStart = minPos;
cInfo[k].interval = minInterval;
cInfo[k].overShoot = max(minEndPos - bestEndPos, 0);
insertStart(StartField, di.nFields, cInfo[k]);
alternator += alteration;
}
}
}
void oEvent::drawRemaining(DrawMethod method, bool placeAfter)
{
DrawType drawType = placeAfter ? DrawType::RemainingAfter : DrawType::RemainingBefore;
@ -1271,7 +1325,7 @@ void oEvent::drawListClumped(int ClassID, int FirstStart, int Interval, int Vaca
void oEvent::automaticDrawAll(gdioutput &gdi, const wstring &firstStart,
const wstring &minIntervall, const wstring &vacances,
bool lateBefore, DrawMethod method, int pairSize)
bool lateBefore, bool allowNeighbourSameCourse, DrawMethod method, int pairSize)
{
gdi.refresh();
const int leg = 0;
@ -1403,6 +1457,7 @@ void oEvent::automaticDrawAll(gdioutput &gdi, const wstring &firstStart,
di.minVacancy = 1;
di.maxVacancy = 100;
di.vacancyFactor = vacancy;
di.allowNeighbourSameCourse = allowNeighbourSameCourse;
di.startName = start;

View File

@ -3136,8 +3136,13 @@ bool oRunner::apply(bool sync, pRunner src, bool setTmpOnly) {
tLeg = -1;
tLegEquClass = 0;
tUseStartPunch=true;
if (tInTeam)
if (tInTeam) {
tInTeam->apply(sync, this, setTmpOnly);
if (Class && Class->isQualificationFinalBaseClass()) {
if (tLeg > 0 && Class == getClassRef(true))
tNeedNoCard = true; // Not qualified
}
}
else {
if (Class && Class->hasMultiCourse()) {
pClass pc=Class;

View File

@ -142,6 +142,12 @@ void pdfwriter::selectFont(HPDF_Page page, const PDFFontSet &fs, int format, flo
}
}
const float fontFromGdiScale(double gdiScale) {
double f = max(1.0, min(gdiScale, 3.0))-1.0;
double s = 1.05 + 0.75*f;
return float(s);
}
void pdfwriter::generatePDF(const gdioutput &gdi,
const wstring &file,
const wstring &pageTitleW,
@ -206,7 +212,9 @@ void pdfwriter::generatePDF(const gdioutput &gdi,
float w = HPDF_Page_GetWidth(page);
float h = HPDF_Page_GetHeight(page);
float scale = (w / maxX) * 0.95f;
float fontScale = 1.5f;
double gdiScale = gdi.getScale();
const float fontScale = fontFromGdiScale(gdiScale);
vector<RenderedPage> pages;
PageInfo pageInfo;
@ -243,33 +251,33 @@ void pdfwriter::generatePDF(const gdioutput &gdi,
if (fonts.count(info[k].ti.font) == 0) {
FontInfo fi;
gdi.getFontInfo(info[k].ti, fi);
float fontScale;
float fontScaleLoc;
wstring tmpFile;
fonts[info[k].ti.font] = fs; //Default fallback
PDFFontSet &f = fonts[info[k].ti.font];
HPDF_Font font = getPDFFont(fi.normal, float(gdi.getScale()), tmpFile, fontScale);
HPDF_Font font = getPDFFont(fi.normal, float(gdi.getScale()), tmpFile, fontScaleLoc);
if (!tmpFile.empty())
tmpFiles.push_back(tmpFile);
if (font) {
f.font = font;
f.fontScale = fontScale;
f.fontScale = fontScaleLoc;
}
font = getPDFFont(fi.italic, float(gdi.getScale()), tmpFile, fontScale);
font = getPDFFont(fi.italic, float(gdi.getScale()), tmpFile, fontScaleLoc);
if (!tmpFile.empty())
tmpFiles.push_back(tmpFile);
if (font) {
f.fontItalic = font;
f.fontScaleItalic = fontScale;
f.fontScaleItalic = fontScaleLoc;
}
font = getPDFFont(fi.bold, (float)gdi.getScale(), tmpFile, fontScale);
font = getPDFFont(fi.bold, (float)gdi.getScale(), tmpFile, fontScaleLoc);
if (!tmpFile.empty())
tmpFiles.push_back(tmpFile);
if (font) {
f.fontBold = font;
f.fontScaleBold = fontScale;
f.fontScaleBold = fontScaleLoc;
}
}

View File

@ -835,7 +835,7 @@ SI X är redan inläst. Ska den läsas in igen? = SI X är redan inläst. Ska de
SI på = SI på
SI-dubbletter: %d = SI-dubbletter: %d
SOFT-avgift = SOFT-avgift
SOFT-lottning = SOFT-lottning
SOFT-lottning = Äldre SOFT-lottning
Saknad starttid = Saknad starttid
Sammanställning = Sammanställning
Sammanställning, ekonomi = Sammanställning, ekonomi