Files
secondo/Algebras/MRegionOps/secondo_buglist.txt
2026-01-23 17:03:45 +08:00

497 lines
14 KiB
Plaintext

Bugliste
********************************************************************************
1 Bug in MRegion::AtPeriods: (TemporalExtAlgebra.cpp)
Offset wird doppelt addiert.
Fix:
In TemporalExtAlgebra.cpp Zeile 1886 entfernen:
utemp->GetSegment(oldsgms, utemp->GetStartPos() + k, oldsmg);
und durch utemp->GetSegment(oldsgms, k, oldsmg); ersetzen.
********************************************************************************
2 Bug in Region::Rotate:
Das Attribut insideAbove der Halbsegmente wird nicht angepasst.
Ergebnis ist inkonsistent und nachfolgende Operationen,
die insideAbove verwenden, scheitern.
Beispiel:
query area(grunewald) -> 32186041
query area(grunewald rotate[1600.0, 7500.0, 90.0]) -> -1.13969e+07
********************************************************************************
3 Bug in InURegionEmbedded: (MovingRegionAlgebra.cpp)
InURegionEmbedded weist manche korrekten Listen als fehlerhaft zurück.
Hintergrund: Region::EndBulkLoad erwartet alle Halbsegmentpaare
fortlaufend ab 0 nummeriert.
Zur Nummerierung wird die edgeno benutzt.
Außerdem muß InsideAbove korrekt gesetzt sein.
Alle Attribute (faceno, cycleno, edgeno, partnerno, coverageno)
werden dann von Region::EndBulkLoad bestimmt.
InURegionEmbedded versucht allerdings diese Attribute vorher selbst zu
setzen, was insbesondere dann schiefgeht,
wenn mehr als ein Cycle im Spiel ist.
Außerdem beginnt die Zählung der egdeno bei 1, anstelle von 0.
Quickfix:
In MovingRegionAlgebra.cpp Zeile 5301 einfügen:
int pointno = -1; (Zählung beginnt dann bei 0)
In MovingRegionAlgebra.cpp Zeile 5333 entfernen:
unsigned int pointno = 0;
(Kein Rücksetzen des Zählers nach jedem Cycle)
********************************************************************************
4 Problem in InURegionEmbedded: (MovingRegionAlgebra.cpp)
InURegionEmbedded weist Units mit einer Intervalllänge
im Sekundenbereich als ungültig zurück.
Hintergrund: In Zeile 5291 werden die double-Darstellungen der
Unitgrenzen mit Hilfe
von AlmostEqual verglichen und durch das relativ große epsilon
als gleich ausgewertet.
********************************************************************************
5 Problem in URegionEmb::TemporalFunction: (MovingRegionAlgebra.cpp)
Falsche Ergebnisse für Unitintervalle im Bereich von Jahrzehnten.
Hintergrund: URegionEmb::TemporalFunction verwendet
"double DateTime::operator/(const DateTime T2)" in
zeile 5071. "/" rechnet intern auf Millisekunden um.
Dies wird für große Werte offenbar numerisch instabil.
Beispiel:
Instant t1(instanttype);
Instant t12(instanttype);
Instant t2(instanttype);
t1.ReadFrom(0.0);
t12.ReadFrom(5000.0);
t2.ReadFrom(10000.0);
cout << "t1: " << t1 << endl;
cout << "t12: " << t12 << endl;
cout << "t2: " << t2 << endl;
cout << (t12 - t1) / (t2 - t1) << endl;
cout << (t12 - t1).ToDouble() / (t2 - t1).ToDouble() << endl;
********************************************************************************
6 Problem mit Region::SetDefined(bool defined)
Beispiel:
let mr = [const movingregion value(((0.0 100.0 TRUE TRUE)
((((0.0 0.0 0.0 0.0)
(0.0 1.0 0.0 1.0)
(1.0 1.0 1.0 1.0)
(1.0 0.0 1.0 0.0))))))];
let t0 = -50.0;
let t1 = 150.0;
let step = (abs(t1 - t0)) / 10.0;
let times = realstream(t0, t1, step)
use[fun(t: real) create_instant(t)] transformstream consume;
query times feed transformstream
use[fun(t: instant) val(mr atinstant t)] transformstream consume;
Die letzte Query liefert ein falsches Ergebnis:
(
(rel
(tuple
(
(elem region))))
(
(
())
(
())
(
())
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))))
Richtig ist aber:
(
(rel
(tuple
(
(elem region))))
(
(
())
(
())
(
())
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
(
(
(
(1.0 0.0)
(0.0 0.0)
(0.0 1.0)
(1.0 1.0)))))
(
())
(
())
(
())))
query val(mr atinstant create_instant(t)) mit t > 100.0 bringt
beispielsweise auch das erwartete Ergebnis einer leeren Region.
Das Problem hängt offenbar mit der Methode Region::SetDefined zusammen,
die gegenwärtig nichts tut.
void Region::SetDefined( bool defined )
{
if (!defined)
Clear();
}
löst das Problem erstmal.
Vermutlich ist es aber etwas tieferliegendes...
********************************************************************************
7 Bug in Region::GetNewFaceNo:
Liefert im allgemeinen ein falsches Ergebnis.
Beispiel:
let rA = [const region value((( (0.0 6.0) (0.0 10.0) (10.0 10.0)
(10.0 0.0) (1.0 0.0) (1.0 3.0) (4.0 3.0) (4.0 6.0) )))];
let rB = [const region value((( (2.0 7.0) (7.0 7.0)
(7.0 8.0) (2.0 8.0) )))];
query minus_new(rA, rB) liefert zwei Flächen.
Richtig ist aber eine Fläche mit einem Loch.
(Im Viewer fällt dies seltsamerweise nicht auf,
wohl aber in der internen Repräsentation und in der Listendarstellung.)
Hintergrund: Region::GetNewFaceNo als Hilfsfunktion von
Region::ComputeRegion entscheidet ob ein neu gefundener
Cycle eine Fläche oder ein Loch ist und gibt gegebenenfalls
die faceno des zugehörigen Außencycles zurück.
Leider ist der Algorithmus fehlerhaft und
funktioniert nur in einfachen Fällen.
Fixvorschlag:
In Region::ComputeRegion muß nur der Aufruf von Region::GetNewFaceNo
geändert werden.
Die Methoden
void Region::GetNewFaceNo(const HalfSegment& hsIn, int startpos)
und bool HalfSegment::RayDown(const Point& p, double &yIntersection)
kommen neu dazu.
void Region::ComputeRegion()
{
//array that stores in position i the last cycle number of the face i
vector<int> face;
//array that stores in the position ~i~ if the half
//segment hi had already the face
//number, the cycle number and the edge number
//attributes set properly, in other words,
//it means that hi is already part of a cycle
bool *cycle;
int lastfaceno=0,
faceno=0,
cycleno = 0,
edgeno = 0;
bool isFirstCHS=true;
const HalfSegment *hs;
if (Size()==0)
return;
//Insert in the vector the first cycle of the first face
face.push_back(0);
cycle = new bool[Size()];
#ifdef SECONDO_MAC_OSX
// something goes wrong at mac osx and the memset function
int size = Size();
for(int i=0;i<size;i++){
cycle[i] = false;
}
#else
memset( cycle, false, Size() );
#endif
for ( int i=0; i<Size(); i++)
{
Get(i,hs);
HalfSegment aux(*hs);
if ( aux.IsLeftDomPoint() && !cycle[i])
{
if(!isFirstCHS)
{
//int facenoAux = GetNewFaceNo(aux,cycle);
int facenoAux = GetNewFaceNo(aux, i);
if (facenoAux==-1)
{/*The lhs half segment will start a new face*/
//cout << "new face: " << aux << endl;
lastfaceno++;
faceno = lastfaceno;
/*to store the first cycle number of the face lastFace*/
face.push_back(0);
cycleno = 0;
edgeno = 0;
}
else
{ /*The half segment ~hs~ belongs to an existing face*/
//cout << "new hole: " << aux << endl;
faceno = facenoAux;
face[faceno]++;
cycleno = face[faceno];
edgeno = 0;
}
}
else
{
isFirstCHS = false;
}
ComputeCycle(aux, faceno,cycleno, edgeno, cycle);
}
}
delete [] cycle;
noComponents = lastfaceno + 1;
}
// This function returns the faceno of
// the first half segment under the half segment hsIn,
// or -1 if hsIn belongs to a new face.
int Region::GetNewFaceNo(const HalfSegment& hsIn, const int startpos) const {
// Precondition:
// hsIn is the smallest (in halfsegment-order) segment of a cycle.
// startpos is the index of hsIn in the DBArray.
if (hsIn.GetAttr().insideAbove) {
// hsIn belongs to a new face:
return -1;
}
// Now we know hsIn belongs to a new hole and we
// have to encounter the enclosing face.
// This is done by searching the next halfsegment maxHS 'under' hsIn.
// Since we go downwards, the facenumber of maxHS must be already known
// and is equal to the facenumber of hsIn.
double y0;
double maxY0;
const HalfSegment *hs = 0;
const HalfSegment *maxHS = 0;
const Point& p = hsIn.GetLeftPoint();
const int coverno = hsIn.GetAttr().coverageno;
int touchedNo = 0;
int i = startpos - 1;
bool first = true;
while (i >=0 && touchedNo < coverno) {
Get(i, hs);
if (!hs->IsLeftDomPoint()) {
i--;
continue;
}
if (hs->GetLeftPoint().GetX() <= p.GetX() &&
p.GetX() <= hs->GetRightPoint().GetX()) {
touchedNo++;
}
if (!AlmostEqual(hs->GetRightPoint().GetX(), p.GetX()) &&
hs->RayDown(p, y0)) {
if (first ||
y0 > maxY0 ||
(AlmostEqual(y0, maxY0) && *hs > *maxHS)) {
// To find the first halfsegment 'under' hsIn
// we compare them as follows:
// (1) y-value of the intersection point between a ray down
// from the left point of hsIn and hs.
// (2) halfsegment order.
maxY0 = y0;
maxHS = hs;
first = false;
}
}
i--;
}
if (maxHS == 0) {
cerr << "Problem in rebuilding cycle in a region " << endl;
cerr << "No outer cycle found" << endl;
cerr << "hsIn: " << hsIn << endl;
cerr << "Halfsegments : --------------- " << endl;
const HalfSegment* hs;
for(int i=0;i<Size();i++) {
Get(i,hs);
cerr << i << " : " << (*hs) << endl;
}
assert(false);
}
//the new cycle is a holecycle of the face ~maxHS.attr.faceno~
return maxHS->GetAttr().faceno;
}
bool HalfSegment::RayDown( const Point& p, double &yIntersection ) const
{
if (this->IsVertical())
return false;
const Coord& x = p.GetX(), y = p.GetY(),
xl = GetLeftPoint().GetX(),
yl = GetLeftPoint().GetY(),
xr = GetRightPoint().GetX(),
yr = GetRightPoint().GetY();
// between is true, iff xl <= x <= xr.
const bool between = CompareDouble(x, xl) != -1 &&
CompareDouble(x, xr) != 1;
if (!between)
return false;
const double k = (yr - yl) / (xr - xl);
const double a = (yl - k * xl);
const double y0 = k * x + a;
if (CompareDouble(y0, y) == 1) // y0 > y: this is above p.
return false;
// y0 <= p: p is above or on this.
yIntersection = y0;
return true;
}