497 lines
14 KiB
Plaintext
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;
|
|
}
|
|
|
|
|
|
|