2015-02-16 17:36:18 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
|
|
|
* Copyright (c) 2014-2015, The OpenClonk Team and contributors
|
|
|
|
*
|
|
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
|
|
* "COPYING" for details.
|
|
|
|
*
|
|
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
|
|
* See accompanying file "TRADEMARK" for details.
|
|
|
|
*
|
|
|
|
* To redistribute this file separately, substitute the full license texts
|
|
|
|
* for the above references.
|
|
|
|
*/
|
2014-10-11 21:13:10 +00:00
|
|
|
|
|
|
|
#include "C4Include.h"
|
2015-06-17 19:30:56 +00:00
|
|
|
|
|
|
|
#ifndef USE_CONSOLE
|
2014-10-12 10:59:25 +00:00
|
|
|
#include "C4FoWBeam.h"
|
2014-10-11 21:13:10 +00:00
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
// Maximum error allowed while merging beams.
|
2014-10-11 21:13:10 +00:00
|
|
|
const int32_t C4FoWMergeThreshold = 10; // (in landscape pixels * 2)
|
|
|
|
|
|
|
|
// A = 1/2 | a x b |
|
|
|
|
static inline int32_t getDoubleTriangleSurface(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3)
|
|
|
|
{
|
|
|
|
int32_t ax = x2 - x1, ay = y2 - y1;
|
|
|
|
int32_t bx = x3 - x1, by = y3 - y1;
|
|
|
|
// We don't bother to actually halve so we can stay with integers.
|
|
|
|
// Doesn't matter as long as we keep in mind the threshold needs to
|
|
|
|
// be doubled.
|
|
|
|
return abs(ax * by - ay * bx);
|
|
|
|
}
|
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
StdStrBuf C4FoWBeam::getDesc() const {
|
2014-10-11 21:13:10 +00:00
|
|
|
return FormatString("%d:%d@%d:%d%s",
|
|
|
|
getLeftX(1000),
|
|
|
|
getRightX(1000),
|
|
|
|
getLeftEndY(),
|
|
|
|
getRightEndY(),
|
|
|
|
fDirty ? "*" : "");
|
|
|
|
}
|
|
|
|
|
2014-11-16 17:57:42 +00:00
|
|
|
bool C4FoWBeam::MergeRight(int32_t x, int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
|
|
|
// Note: Right-merging is the most common and most important optimization.
|
|
|
|
// This procedure will probably be *hammered* as a result. Worth inlining?
|
|
|
|
|
|
|
|
assert(!isDirty()); assert(isRight(x, y));
|
|
|
|
|
|
|
|
// Calculate error. Note that simply summing up errors is not correct,
|
|
|
|
// strictly speaking (as new and old error surfaces might overlap). Still,
|
2015-06-17 19:30:56 +00:00
|
|
|
// this is quite elaborate already, no need to make it even more
|
2014-10-11 21:13:10 +00:00
|
|
|
int32_t iErr = getDoubleTriangleSurface(
|
|
|
|
getLeftEndX(), iLeftEndY,
|
|
|
|
getRightEndX(), iRightEndY,
|
|
|
|
x, y);
|
|
|
|
if (iError + iErr > C4FoWMergeThreshold)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Move right endpoint.
|
|
|
|
iRightX = x;
|
|
|
|
iRightY = y;
|
|
|
|
iRightEndY = y;
|
|
|
|
iError += iErr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-11-16 17:57:42 +00:00
|
|
|
bool C4FoWBeam::MergeLeft(int32_t x, int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
|
|
|
assert(!isDirty()); assert(isLeft(x, y));
|
|
|
|
|
|
|
|
// Calculate error.
|
|
|
|
float iErr = getDoubleTriangleSurface(
|
|
|
|
getLeftEndX(), iLeftEndY,
|
|
|
|
getRightEndX(), iRightEndY,
|
|
|
|
x, y);
|
|
|
|
if (iError + iErr > C4FoWMergeThreshold)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Move left endpoint.
|
|
|
|
iLeftX = x;
|
|
|
|
iLeftY = y;
|
|
|
|
iLeftEndY = y;
|
|
|
|
iError += iErr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-11-16 17:57:42 +00:00
|
|
|
bool C4FoWBeam::EliminateRight(int32_t x, int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
2014-10-12 10:59:25 +00:00
|
|
|
// Called on the beams left of the one getting eliminated
|
|
|
|
C4FoWBeam *pElim = pNext, *pMerge = pNext->pNext;
|
2014-10-11 21:13:10 +00:00
|
|
|
assert(!!pElim); assert(!!pMerge);
|
|
|
|
assert(!isDirty()); assert(!pMerge->isDirty());
|
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
// Calc errors, add those accumulated on both merged beams
|
2014-10-11 21:13:10 +00:00
|
|
|
int32_t iErr = getDoubleTriangleSurface(
|
2014-10-12 19:04:44 +00:00
|
|
|
getLeftEndX(), getLeftEndY(),
|
|
|
|
pMerge->getRightEndX(), pMerge->getRightEndY(),
|
2014-10-11 21:13:10 +00:00
|
|
|
x, y);
|
2014-10-12 19:04:44 +00:00
|
|
|
iErr += pMerge->iError;
|
|
|
|
if (iError + iErr > C4FoWMergeThreshold)
|
2014-10-11 21:13:10 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Do elimination
|
|
|
|
iRightX = pMerge->iRightX;
|
|
|
|
iRightY = pMerge->iRightY;
|
|
|
|
iRightEndY = pMerge->iRightEndY;
|
|
|
|
pNext = pMerge->pNext;
|
2014-10-12 19:04:44 +00:00
|
|
|
iError += iErr;
|
2014-10-11 21:13:10 +00:00
|
|
|
delete pElim;
|
|
|
|
delete pMerge;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-11-16 17:57:42 +00:00
|
|
|
C4FoWBeam *C4FoWBeam::Split(int32_t x, int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
2014-10-12 10:59:25 +00:00
|
|
|
// Make sure to never create negative-surface beams
|
2014-10-11 21:13:10 +00:00
|
|
|
assert(isDirty()); assert(isInside(x, y));
|
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
// Allocate a new beam. Ugh, expensive.
|
|
|
|
C4FoWBeam *pBeam = new C4FoWBeam(x, y, iRightX, iRightY);
|
|
|
|
pBeam->Dirty(iLeftEndY);
|
2014-10-11 21:13:10 +00:00
|
|
|
|
|
|
|
// Move to make space
|
|
|
|
iRightX = x;
|
|
|
|
iRightY = y;
|
|
|
|
|
|
|
|
// Relink
|
2014-10-12 10:59:25 +00:00
|
|
|
pBeam->pNext = pNext;
|
|
|
|
pNext = pBeam;
|
|
|
|
return pBeam;
|
2014-10-11 21:13:10 +00:00
|
|
|
}
|
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
void C4FoWBeam::MergeDirty()
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
2014-10-12 10:59:25 +00:00
|
|
|
// As a rule, dirty beams following each other should
|
2014-10-11 21:13:10 +00:00
|
|
|
// always be merged, so splits can be reverted once
|
|
|
|
// the landscape changes.
|
2014-10-12 10:59:25 +00:00
|
|
|
C4FoWBeam *pWith = pNext;
|
2014-10-11 21:13:10 +00:00
|
|
|
assert(isDirty()); assert(!!pWith); assert(pWith->isDirty());
|
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
// Figure out how far the new dirty beams reaches. Note that
|
2014-10-11 21:13:10 +00:00
|
|
|
// we might lose information about the landscape here.
|
|
|
|
Dirty(Min(getLeftEndY(), pWith->getLeftEndY()));
|
|
|
|
|
|
|
|
// Set right
|
|
|
|
iRightX = pWith->iRightX;
|
|
|
|
iRightY = pWith->iRightY;
|
|
|
|
|
|
|
|
// Relink & delete
|
|
|
|
pNext = pWith->getNext();
|
|
|
|
delete pWith;
|
|
|
|
}
|
|
|
|
|
2014-11-16 17:57:42 +00:00
|
|
|
void C4FoWBeam::Clean(int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
2014-10-12 10:59:25 +00:00
|
|
|
// Search hit something, this beam is now clean.
|
2014-10-11 21:13:10 +00:00
|
|
|
iLeftEndY = y;
|
|
|
|
iRightEndY = y;
|
|
|
|
fDirty = false;
|
|
|
|
}
|
|
|
|
|
2014-11-16 17:57:42 +00:00
|
|
|
void C4FoWBeam::Dirty(int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
2014-10-12 10:59:25 +00:00
|
|
|
// Got invalidated, beam is dirty until updated
|
2014-10-11 21:13:10 +00:00
|
|
|
iLeftEndY = y;
|
|
|
|
iRightEndY = y;
|
|
|
|
fDirty = true;
|
|
|
|
}
|
|
|
|
|
2014-10-12 10:59:25 +00:00
|
|
|
void C4FoWBeam::Prune(int32_t y)
|
2014-10-11 21:13:10 +00:00
|
|
|
{
|
|
|
|
// Check which sides we need to prune
|
|
|
|
bool fLeft = (iLeftEndY >= y),
|
|
|
|
fRight = (iRightEndY >= y);
|
|
|
|
// If both sides got pruned, we are clean
|
2014-10-12 10:59:25 +00:00
|
|
|
// (can't possibly extend this beam further)
|
2014-10-11 21:13:10 +00:00
|
|
|
if (fLeft && fRight)
|
|
|
|
Clean(y);
|
|
|
|
else if (fLeft)
|
|
|
|
iLeftEndY = y;
|
|
|
|
if (fRight)
|
|
|
|
iRightEndY = y;
|
2015-02-17 00:30:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void C4FoWBeam::CompileFunc(StdCompiler *pComp)
|
|
|
|
{
|
|
|
|
pComp->Value(mkNamingAdapt(iLeftX, "iLeftX"));
|
|
|
|
pComp->Value(mkNamingAdapt(iLeftY, "iLeftY"));
|
|
|
|
pComp->Value(mkNamingAdapt(iRightX, "iRightX"));
|
|
|
|
pComp->Value(mkNamingAdapt(iRightY, "iRightY"));
|
|
|
|
pComp->Value(mkNamingAdapt(iLeftEndY, "iLeftEndY"));
|
|
|
|
pComp->Value(mkNamingAdapt(iRightEndY, "iRightEndY"));
|
|
|
|
pComp->Value(mkNamingAdapt(iError, "iError"));
|
|
|
|
pComp->Value(mkNamingAdapt(fDirty, "fDirty"));
|
|
|
|
}
|
2015-06-17 19:30:56 +00:00
|
|
|
|
|
|
|
#endif
|