/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "b3dcompo.hxx" #include "base3d.hxx" #include "b3dgeom.hxx" #ifndef _INC_FLOAT #include #endif #include namespace binfilter { /************************************************************************* |* |* Vergleiche fuer doubles mit bound |* \************************************************************************/ #define DOUBLE_EQUAL(a,b) (fabs(a-b) < SMALL_DVALUE) #define DOUBLE_NOT_EQUAL(a,b) (fabs(a-b) > SMALL_DVALUE) #define DOUBLE_SMALLER(a,b) ((a + (SMALL_DVALUE / 2.0)) < b) #define DOUBLE_BIGGER(a,b) ((a - (SMALL_DVALUE / 2.0)) > b) /************************************************************************* |* |* Bucket fuer Kantenliste, vertikaler Teil |* \************************************************************************/ SV_IMPL_VARARR(B3dEdgeListBucketMemArr, char*) B3dEdgeListBucket::B3dEdgeListBucket(UINT16 TheSize) { InitializeSize(TheSize); } void B3dEdgeListBucket::InitializeSize(UINT16 TheSize) { UINT16 nSiz; for(nShift=0,nSiz=1;nSiz> nShift); Empty(); } void B3dEdgeListBucket::operator=(const B3dEdgeListBucket& rObj) { Erase(); B3dEdgeListBucket& rSrc = (B3dEdgeListBucket&)rObj; for(UINT32 a=0;a= nCount) { OSL_FAIL("Access to Bucket out of range!"); return *((B3dEdgeList*)aMemArray[0]); } return *((B3dEdgeList*)(aMemArray[(UINT16)(nPos >> nBlockShift)] + ((nPos & nMask) << nShift))); } /************************************************************************* |* |* Bucket fuer Kantenliste, horizontaler Teil |* \************************************************************************/ SV_IMPL_VARARR(B3dEdgeEntryBucketMemArr, char*) B3dEdgeEntryBucket::B3dEdgeEntryBucket(UINT16 TheSize) { InitializeSize(TheSize); } void B3dEdgeEntryBucket::InitializeSize(UINT16 TheSize) { UINT16 nSiz; for(nShift=0,nSiz=1;nSiz> nShift); Empty(); } void B3dEdgeEntryBucket::operator=(const B3dEdgeEntryBucket& rObj) { Erase(); B3dEdgeEntryBucket& rSrc = (B3dEdgeEntryBucket&)rObj; for(UINT32 a=0;a= nCount) { OSL_FAIL("Access to Bucket out of range!"); return *((B3dEdgeEntry*)aMemArray[0]); } return *((B3dEdgeEntry*)(aMemArray[(UINT16)(nPos >> nBlockShift)] + ((nPos & nMask) << nShift))); } /************************************************************************* |* |* Konstruktor |* \************************************************************************/ B3dComplexPolygon::B3dComplexPolygon() : aEntityBuffer(14), // 16K aEdgeList(12), // 4K aEdgeEntry(12) // 4K { EmptyBuffers(); bTestForCut = TRUE; nHighestEdge = 0L; // pBase3D = NULL; pGeometry = NULL; pLastVertex = NULL; } /************************************************************************* |* |* Gib einen neuen freien Eintrag zurueck |* \************************************************************************/ B3dEntity &B3dComplexPolygon::GetFreeEntity() { aEntityBuffer.Append(); return aEntityBuffer[aEntityBuffer.Count() - 1]; } /************************************************************************* |* |* Ein neuer Punkt ist ausgefuellt |* \************************************************************************/ void B3dComplexPolygon::PostAddVertex(B3dEntity &rVertex) { if(pLastVertex && ArePointsEqual(*pLastVertex, rVertex)) { aEntityBuffer.Remove(); return; } // #i27242# // Comparison for finding nHighestEdge has to start with first point. Due // to having this part inside of if(pLastVertex) it always started with the // second point. Thus, in 50% of the cases where the first point of the polygon // is the extreme point, the normal could be wrong. if(!nNewPolyStart) { if(nHighestEdge) TestHighestEdge(rVertex); else nHighestEdge = aEntityBuffer.Count(); } // Zeiger auf letzten hinzugefuegten Punkt setzen pLastVertex = &rVertex; } /************************************************************************* |* |* Testet, ob die neue Edge in allen Freiheitsgraden groesser ist |* als die momentane |* \************************************************************************/ void B3dComplexPolygon::TestHighestEdge(B3dEntity& rVertex) { B3dEntity& rHighest = aEntityBuffer[nHighestEdge - 1]; if(rVertex.GetX() <= rHighest.GetX()) { if(rVertex.GetX() < rHighest.GetX()) { nHighestEdge = aEntityBuffer.Count(); } else { if(rVertex.GetY() <= rHighest.GetY()) { if(rVertex.GetY() < rHighest.GetY()) { nHighestEdge = aEntityBuffer.Count(); } else { if(rVertex.GetZ() < rHighest.GetZ()) { nHighestEdge = aEntityBuffer.Count(); } } } } } } /************************************************************************* |* |* Vergleicht zwei Punkte auf ihren INHALT |* und fuellt die 2D-Koordinaten aus |* \************************************************************************/ BOOL B3dComplexPolygon::ArePointsEqual(B3dEntity& rFirst, B3dEntity& rSecond) { // Wenn der Punkt dem letzten gleich ist, gar nicht behandeln if(rFirst.Point().GetVector3D() == rSecond.Point().GetVector3D()) return TRUE; return FALSE; } /************************************************************************* |* |* Alles auf Startzustand, buffer leeren |* \************************************************************************/ void B3dComplexPolygon::EmptyBuffers() { aEntityBuffer.Erase(); nNewPolyStart = 0; bOrientationValid = FALSE; bNormalValid = FALSE; // EdgeList und EdgeEntries leeren pEdgeList = NULL; aEdgeList.Erase(); aEdgeEntry.Erase(); } /************************************************************************* |* |* Neues Teilpolygon beginnen |* \************************************************************************/ void B3dComplexPolygon::StartPrimitive() { // Bisherige Punkte verarbeiten if(aEntityBuffer.Count() > nNewPolyStart) ComputeLastPolygon(); // Zeiger auf letzten Punkt loeschen pLastVertex = NULL; // Hoechten Punkt vergesset nHighestEdge = 0L; } /************************************************************************* |* |* Teilpolygon abschliessen |* \************************************************************************/ void B3dComplexPolygon::ComputeLastPolygon(BOOL bIsLast) { // Letzten Punkt mit erstem vergleichen, evtl // wegschmeissen if(pLastVertex) { if(ArePointsEqual(aEntityBuffer[nNewPolyStart], *pLastVertex)) { // HighestEdge korrigieren, falls dieser geloescht werden soll if(nHighestEdge && nHighestEdge == aEntityBuffer.Count()) nHighestEdge = nNewPolyStart + 1; aEntityBuffer.Remove(); } } // Sind noch genug Punkte da? if(aEntityBuffer.Count() < nNewPolyStart + 3) { if(pGeometry) { pGeometry->StartComplexPrimitive(); for(UINT32 a=0; a < aEntityBuffer.Count(); a++) pGeometry->AddComplexVertex(aEntityBuffer[a], aEntityBuffer[a].IsEdgeVisible()); pGeometry->EndComplexPrimitive(); } } else { if(!nNewPolyStart && bIsLast && IsConvexPolygon()) { if(pGeometry) { pGeometry->StartComplexPrimitive(); if(aEntityBuffer.Count() > 4) { B3dEntity aNew; aNew.CalcMiddle(aEntityBuffer[0], aEntityBuffer[aEntityBuffer.Count() / 2]); pGeometry->AddComplexVertex(aNew, FALSE); for(UINT32 a=0; a < aEntityBuffer.Count(); a++) pGeometry->AddComplexVertex(aEntityBuffer[a], aEntityBuffer[a].IsEdgeVisible()); pGeometry->AddComplexVertex(aEntityBuffer[0], FALSE); } else { for(UINT32 a=0; a < aEntityBuffer.Count(); a++) pGeometry->AddComplexVertex(aEntityBuffer[a], aEntityBuffer[a].IsEdgeVisible()); } pGeometry->EndComplexPrimitive(); } } else { if(!bNormalValid) ChooseNormal(); // Einsortieren UINT32 nUpperBound = aEntityBuffer.Count(); // Als Polygon behandeln if(GetTestForCut()) { UINT32 a; for(a=nNewPolyStart + 1; a < nUpperBound; a++) AddEdgeCut(&aEntityBuffer[a-1], &aEntityBuffer[a]); // Polygon schliessen AddEdgeCut(&aEntityBuffer[a-1], &aEntityBuffer[nNewPolyStart]); } else { UINT32 a; for(a=nNewPolyStart + 1; a < nUpperBound; a++) AddEdge(&aEntityBuffer[a-1], &aEntityBuffer[a]); // Polygon schliessen AddEdge(&aEntityBuffer[a-1], &aEntityBuffer[nNewPolyStart]); } // Hier setzen, da evtl. bereits neue Punkte // durch Schnitte hinzugekommen sind nNewPolyStart = aEntityBuffer.Count(); } } } /************************************************************************* |* |* Orientierung des ersten Polygons ermitteln |* \************************************************************************/ void B3dComplexPolygon::ChooseNormal() { if(nHighestEdge) { UINT32 nHigh = nHighestEdge - 1; UINT32 nPrev = (nHigh != 0) ? nHigh - 1 : aEntityBuffer.Count() - 1; UINT32 nNext = (nHigh + 1 != aEntityBuffer.Count()) ? nHigh + 1 : nNewPolyStart; // Punkt, Vorgaenger und Nachfolger holen const Vector3D& rHigh = aEntityBuffer[nHigh].Point().GetVector3D(); const Vector3D& rPrev = aEntityBuffer[nPrev].Point().GetVector3D(); const Vector3D& rNext = aEntityBuffer[nNext].Point().GetVector3D(); // Normale bilden aNormal = (rPrev - rHigh)|(rNext - rHigh); if(aNormal != Vector3D()) aNormal.Normalize(); else aNormal = Vector3D(0.0, 0.0, -1.0); } bNormalValid = TRUE; } /************************************************************************* |* |* Komplexes Polygon ausgeben |* \************************************************************************/ void B3dComplexPolygon::EndPrimitive(B3dGeometry *pGeom) { // Funktionszeiger setzen pGeometry = pGeom; // Letztes angefangenes Poly verarbeiten ComputeLastPolygon(TRUE); // Dreiecke generieren und ausgeben while(pEdgeList) ExtractTriangle(); // Buffer leeren EmptyBuffers(); // Zeiger wieder loeschen pGeometry = NULL; } /************************************************************************* |* |* Teste aktuelles Polygon (0..aEntityBuffer.Count()) auf Konvexitaet |* \************************************************************************/ BOOL B3dComplexPolygon::IsConvexPolygon() { B3dEntity* pFirst = &aEntityBuffer[aEntityBuffer.Count() - 2]; B3dEntity* pSecond = &aEntityBuffer[aEntityBuffer.Count() - 1]; B3dEntity* pThird = &aEntityBuffer[0]; BOOL bDirection = IsLeft(pSecond, pFirst, pThird); BOOL bOrder = CompareOrder(pSecond, pThird); UINT16 nDirChanges(0); for(UINT32 a = 1; nDirChanges <= 2 && a < aEntityBuffer.Count(); a++) { pFirst = pSecond; pSecond = pThird; pThird = &aEntityBuffer[a]; if(IsLeft(pSecond, pFirst, pThird) != bDirection) return FALSE; if(CompareOrder(pSecond, pThird) != bOrder) { nDirChanges++; bOrder = !bOrder; } } // Zuviele aenderungen der Ordnung, auf keinen Fall Convex if(nDirChanges > 2) return FALSE; return TRUE; } /************************************************************************* |* |* Lexikografische Ordnung der beiden Punkte |* \************************************************************************/ BOOL B3dComplexPolygon::CompareOrder(B3dEntity* pFirst, B3dEntity* pSecond) { if(pFirst->GetX() < pSecond->GetX()) return FALSE; if(pFirst->GetX() > pSecond->GetX()) return TRUE; if(pFirst->GetY() < pSecond->GetY()) return FALSE; return TRUE; } /************************************************************************* |* |* Teste, ob die Punkte der Kante getauscht werden muessen |* \************************************************************************/ BOOL B3dComplexPolygon::DoSwap(B3dEntity* pStart, B3dEntity* pEnd) { if(DOUBLE_EQUAL(pStart->GetY(), pEnd->GetY())) { if(pStart->GetX() > pEnd->GetX()) return TRUE; } else { if(pStart->GetY() > pEnd->GetY()) return TRUE; } return FALSE; } /************************************************************************* |* |* Kante nInd1, nInd2 zu Kantenliste hinzufuegen |* \************************************************************************/ B3dEdgeEntry* B3dComplexPolygon::AddEdge(B3dEntity* pStart, B3dEntity* pEnd) { if(DoSwap(pStart, pEnd)) return InsertEdge(GetList(pEnd), pStart, TRUE); return InsertEdge(GetList(pStart), pEnd, TRUE); } /************************************************************************* |* |* Einen Listeneintrag suchen oder neu anlegen |* Liefert immer einen Listeneintrag zurueck |* \************************************************************************/ B3dEdgeList* B3dComplexPolygon::GetList(B3dEntity* pStart) { B3dEdgeList* pList = pEdgeList; B3dEdgeList* pLast = NULL; while(pList && pList->GetStart() != pStart && DoSwap(pStart, pList->GetStart())) { pLast = pList; pList = pList->GetDown(); } if(pList) { if(pList->GetStart() != pStart) { if(DOUBLE_NOT_EQUAL(pStart->GetX(), pList->GetXPos()) || DOUBLE_NOT_EQUAL(pStart->GetY(), pList->GetYPos())) { // Auf jeden Fall ein neuer Eintrag aEdgeList.Append(); B3dEdgeList* pNewList = &aEdgeList[aEdgeList.Count() - 1]; pNewList->Reset(); pNewList->SetStart(pStart); // vor pList einhaengen // pLast KANN NULL SEIN! pNewList->SetDown(pList); pList->SetParent(pNewList); if(pLast) { pNewList->SetParent(pLast); pLast->SetDown(pNewList); } else { pEdgeList = pNewList; } // Returnwert setzen pList = pNewList; } else { // pList->GetStart() != pStart, aber // die Koordinaten sind praktisch identisch! // Gib diese Liste zurueck, d.h. // tue gar nichts } } } else { // pLast->GetYPos() < pStart->GetY(), // Hinten anhaengen aEdgeList.Append(); pList = &aEdgeList[aEdgeList.Count() - 1]; pList->Reset(); pList->SetStart(pStart); if(pLast) { pList->SetParent(pLast); pLast->SetDown(pList); } else { pEdgeList = pList; } } return pList; } /************************************************************************* |* |* Eine Kante in eine Kantenliste einsortieren |* Die Kante wird dabei neu erzeugt |* \************************************************************************/ B3dEdgeEntry* B3dComplexPolygon::InsertEdge(B3dEdgeList* pList, B3dEntity* pEnd, BOOL bEdgeVisible) { B3dEdgeEntry* pEntry = pList->GetEntries(); // Immer ein neuer Eintrag aEdgeEntry.Append(); B3dEdgeEntry* pNewEntry = &aEdgeEntry[aEdgeEntry.Count() - 1]; pNewEntry->Reset(); pNewEntry->SetEnd(pEnd); pNewEntry->SetParent(pList); pNewEntry->SetEdgeVisible(bEdgeVisible); if(pEntry) { B3dEdgeEntry* pLast = NULL; double fSlant = GetSlant(pNewEntry); while(pEntry && GetSlant(pEntry) < fSlant) { pLast = pEntry; pEntry = pEntry->GetRight(); } if(pEntry) { // GetSlant(pEntry) < fSlant // GetSlant(pLast) >= fSlant // Neuen Eintrag hinter pLast einfuegen // pLast KANN NULL SEIN! pNewEntry->SetRight(pEntry); if(pLast) { pLast->SetRight(pNewEntry); } else { pList->SetEntries(pNewEntry); } } else { // GetSlant(pEntry) >= fSlant // Neuen Eintrag am Ende anhaengen pLast->SetRight(pNewEntry); } } else { pList->SetEntries(pNewEntry); } // Returnwert return pNewEntry; } /************************************************************************* |* |* Steigung der Kante liefern |* \************************************************************************/ double B3dComplexPolygon::GetSlant(B3dEdgeEntry* pEdge) { double fDivisor = pEdge->GetYPos() - pEdge->GetParent()->GetYPos(); if(fabs(fDivisor) < SMALL_DVALUE) return DBL_MAX; return (pEdge->GetXPos() - pEdge->GetParent()->GetXPos()) / fDivisor; } /************************************************************************* |* |* Auf Schnitt mit einer vorhandenen Kante testen |* \************************************************************************/ void B3dComplexPolygon::TestForCut(B3dEdgeEntry* pEntry) { // pEntry: die bereits eingefuegte neue Kante, die mit allen // aelteren Kanten geschnitten werden soll B3dEdgeList* pList = pEdgeList; while(pList && DOUBLE_SMALLER(pList->GetYPos(), pEntry->GetYPos())) { // nur in Gruppen mit anderem Startpunkt suchen if(pList != pEntry->GetParent()) { B3dEdgeEntry* pTestEntry = pList->GetEntries(); while(pTestEntry) { if(DOUBLE_BIGGER(pTestEntry->GetYPos(), pEntry->GetParent()->GetYPos())) { // es existiert eine vertikale Bereichsueberschneidung // Min/Max fuer pEntry holen double fXMin = pEntry->GetXPos(); double fXMax = pEntry->GetParent()->GetXPos(); if(fXMin > fXMax) { double fSwap = fXMin; fXMin = fXMax; fXMax = fSwap; } // Min/Max in X fuer Kandidat holen double fTestXMin = pTestEntry->GetXPos(); double fTestXMax = pList->GetXPos(); if(fTestXMin > fTestXMax) { double fSwap = fTestXMin; fTestXMin = fTestXMax; fTestXMax = fSwap; } if(fTestXMin < fXMax && fTestXMax > fXMin) { // es existiert eine horizontale Bereichsueberschneidung // ein Schnitt ist moeglich double fCut = FindCut(pEntry, pTestEntry); if(fCut != 0.0) { // Schnitt existiert! fCut ist aus dem Parameterbereich // der ersten Kante, also pEntry. Neuen Punkt erzeugen. B3dEntity& rNew = GetFreeEntity(); rNew.CalcInBetween(*pEntry->GetParent()->GetStart(), *pEntry->GetEnd(), fCut); // Neuen Punkt und neue von diesem ausgehende Kanten erzeugen B3dEdgeList* pNewPointList = GetList(&rNew); B3dEdgeEntry* pEntry2 = InsertEdge(pNewPointList, pEntry->GetEnd(), pEntry->IsEdgeVisible()); InsertEdge(pNewPointList, pTestEntry->GetEnd(), pTestEntry->IsEdgeVisible()); // Beteiligte Entries kuerzen pEntry->SetEnd(&rNew); pTestEntry->SetEnd(&rNew); // Das neue Ende von pEntry kann weitere Linien // schneiden, also geht der test mit diesem weiter TestForCut(pEntry2); // Test mit gekuerztem pEntry fortsetzen } } } // naechster Entry pTestEntry = pTestEntry->GetRight(); } } // naechste Liste pList = pList->GetDown(); } } /************************************************************************* |* |* Berechne den Schnitt zwischen den beiden Kanten und gib den |* Schnittpunkt im Parameterbereich der 1. Kante zurueck |* \************************************************************************/ double B3dComplexPolygon::FindCut(B3dEdgeEntry* pEdge1, B3dEdgeEntry* pEdge2) { double fRetval = 0.0; double fDeltaEdge2Y = pEdge2->GetYPos() - pEdge2->GetParent()->GetYPos(); double fDeltaEdge2X = pEdge2->GetXPos() - pEdge2->GetParent()->GetXPos(); double fDeltaEdge1X = pEdge1->GetXPos() - pEdge1->GetParent()->GetXPos(); double fDeltaEdge1Y = pEdge1->GetYPos() - pEdge1->GetParent()->GetYPos(); // Dynamische Grenze fuer parallelitaet berechnen double fSmallValue = fabs((fDeltaEdge2Y + fDeltaEdge2X + fDeltaEdge1X + fDeltaEdge1Y) * (SMALL_DVALUE / 4.0)); double fZwi = (fDeltaEdge1X * fDeltaEdge2Y) - (fDeltaEdge1Y * fDeltaEdge2X); if(fabs(fZwi) > fSmallValue) { fZwi = (fDeltaEdge2Y * (pEdge2->GetParent()->GetXPos() - pEdge1->GetParent()->GetXPos()) + fDeltaEdge2X * (pEdge1->GetParent()->GetYPos() - pEdge2->GetParent()->GetYPos())) / fZwi; // Im Parameterbereich der ersten Kante (ohne Punkte) ? if(fZwi > fSmallValue && fZwi < 1.0 - fSmallValue) { // Schnitt liegt im Parameterbereich der ersten // Linie, aber auch in dem der zweiten? if(fabs(fDeltaEdge2X) > fSmallValue && fabs(fDeltaEdge2X) > fabs(fDeltaEdge2Y)) { fDeltaEdge2Y = (pEdge1->GetParent()->GetXPos() + fZwi * fDeltaEdge1X - pEdge2->GetParent()->GetXPos()) / fDeltaEdge2X; // Parameterbereich der zweiten schliesst Start/Ende mit ein! if(fDeltaEdge2Y > -fSmallValue && fDeltaEdge2Y < 1.0 + fSmallValue) { // Ja. Zuweisen. fRetval = fZwi; } } else if(fabs(fDeltaEdge2Y) > fSmallValue) { fDeltaEdge2X = (pEdge1->GetParent()->GetYPos() + fZwi * fDeltaEdge1Y - pEdge2->GetParent()->GetYPos()) / fDeltaEdge2Y; // Parameterbereich der zweiten schliesst Start/Ende mit ein! if(fDeltaEdge2X > -fSmallValue && fDeltaEdge2X < 1.0 + fSmallValue) { // Ja. Zuweisen. fRetval = fZwi; } } } } return fRetval; } /************************************************************************* |* |* Testet, ob die angegebene Kante schon existiert |* Ja: Entfernen |* Nein: Einfuegen |* \************************************************************************/ BOOL B3dComplexPolygon::SwitchEdgeExistance(B3dEntity* pStart, B3dEntity* pEnd) { if(DoSwap(pStart, pEnd)) { B3dEntity* pZwi = pStart; pStart = pEnd; pEnd = pZwi; } if(pEdgeList) { // Suchen B3dEdgeList* pList = pEdgeList; while(pList && pList->GetStart() != pStart) pList = pList->GetDown(); if(pList && pList->GetStart() == pStart) { // Liste gefunden, Eintrag mit Endpunkt // pEnd finden B3dEdgeEntry* pEntry = pList->GetEntries(); B3dEdgeEntry* pLeft = NULL; while(pEntry) { if(pEntry->GetEnd() == pEnd) { // Kante existiert, austragen // Liste ist pList // Links ist pLeft if(pLeft) { pLeft->SetRight(pEntry->GetRight()); } else { if(pEntry->GetRight()) pList->SetEntries(pEntry->GetRight()); else RemoveEdgeList(pList); } // fertig return TRUE; } // naechste Kante pLeft = pEntry; pEntry = pEntry->GetRight(); } // Liste existiert, aber der EdgeEintrag nicht. // Fuege diesen hinzu InsertEdge(pList, pEnd, FALSE); // fertig return FALSE; } } // Liste und Eintrag existieren nicht // Erzeuge beides InsertEdge(GetList(pStart), pEnd, FALSE); return FALSE; } /************************************************************************* |* |* Entferne die Kante aus der Kantenliste. Tue alles weitere, |* um die Struktur weiter aufzuloesen |* \************************************************************************/ void B3dComplexPolygon::RemoveFirstEdge(B3dEdgeList* pList) { if(pList->GetEntries()->GetRight()) pList->SetEntries(pList->GetEntries()->GetRight()); else RemoveEdgeList(pList); } /************************************************************************* |* |* Entferne die Kantenliste. Tue alles weitere, |* um die Struktur weiter aufzuloesen |* \************************************************************************/ void B3dComplexPolygon::RemoveEdgeList(B3dEdgeList* pList) { if(pList->GetDown()) pList->GetDown()->SetParent(pList->GetParent()); if(pList->GetParent()) pList->GetParent()->SetDown(pList->GetDown()); else { // Es gibt keinen parent mehr pEdgeList = pList->GetDown(); } } /************************************************************************* |* |* Extrahiere das naechste Dreieck aus der Kantenliste |* und zeichne es |* \************************************************************************/ void B3dComplexPolygon::ExtractTriangle() { B3dEdgeEntry* pLeft = pEdgeList->GetEntries(); B3dEdgeEntry* pRight = pLeft->GetRight(); if(!pRight) { RemoveFirstEdge(pEdgeList); return; } B3dEdgeList* pList = FindStartInTriangle(); BOOL bNotAllAligned = (fabs(GetSlant(pLeft) - GetSlant(pRight)) > SMALL_DVALUE); BOOL bStartIsEdgePoint = FALSE; if(pList) { const Vector3D& rListStart = pList->GetStart()->Point().GetVector3D(); if((rListStart - pEdgeList->GetStart()->Point().GetVector3D()).GetLength() < SMALL_DVALUE) bStartIsEdgePoint = TRUE; else if((rListStart - pLeft->GetEnd()->Point().GetVector3D()).GetLength() < SMALL_DVALUE) bStartIsEdgePoint = TRUE; else if((rListStart - pRight->GetEnd()->Point().GetVector3D()).GetLength() < SMALL_DVALUE) bStartIsEdgePoint = TRUE; } if(pList && bNotAllAligned && !bStartIsEdgePoint) { // Zerlegen in 2 Teildreiecke // Erstes Teildreieck InsertEdge(pEdgeList, pList->GetStart(), FALSE); ExtractTriangle(); // Zweites Teildreieck InsertEdge(pEdgeList, pList->GetStart(), FALSE); ExtractTriangle(); } else { B3dEntity* pEntLeft = pLeft->GetEnd(); B3dEntity* pEntRight = pRight->GetEnd(); B3dEntity* pEntTop = pEdgeList->GetStart(); BOOL bLeftVisible = pLeft->IsEdgeVisible(); BOOL bRightVisible = pRight->IsEdgeVisible(); RemoveFirstEdge(pEdgeList); RemoveFirstEdge(pEdgeList); if(pEntLeft != pEntRight) { // Merken, ob die Abschlusslinie existiert hat oder nicht BOOL bDidEdgeExist = SwitchEdgeExistance(pEntLeft, pEntRight); if(DOUBLE_NOT_EQUAL(pEntLeft->GetY(), pEntTop->GetY()) || DOUBLE_NOT_EQUAL(pEntRight->GetY(), pEntTop->GetY())) { if(!bOrientationValid) { // Anhand des ersten Dreiecks entscheiden, // in welcher Orientierung die Dreiecke // auszugeben sind Vector3D aTmpNormal = (pEntLeft->Point().GetVector3D() - pEntTop->Point().GetVector3D()) |(pEntRight->Point().GetVector3D() - pEntTop->Point().GetVector3D()); bOrientation = (aNormal.Scalar(aTmpNormal) > 0.0) ? TRUE : FALSE; bOrientationValid = TRUE; } if(pGeometry) { pGeometry->StartComplexPrimitive(); if(bOrientation) { // Rechtsrum pGeometry->AddComplexVertex(*pEntTop, bRightVisible); pGeometry->AddComplexVertex(*pEntRight, bDidEdgeExist); pGeometry->AddComplexVertex(*pEntLeft, bLeftVisible); } else { // Linksrum pGeometry->AddComplexVertex(*pEntTop, bLeftVisible); pGeometry->AddComplexVertex(*pEntLeft, bDidEdgeExist); pGeometry->AddComplexVertex(*pEntRight, bRightVisible); } pGeometry->EndComplexPrimitive(); } } } } } /************************************************************************* |* |* Suche nach einem fremden Startpunkt innerhalb des zu zeichnenden |* naechsten Dreiecks |* \************************************************************************/ B3dEdgeList* B3dComplexPolygon::FindStartInTriangle() { B3dEdgeList* pList = pEdgeList->GetDown(); if(pList) { B3dEdgeEntry* pLeft = pEdgeList->GetEntries(); B3dEdgeEntry* pRight = pLeft->GetRight(); double fYMax = pLeft->GetYPos(); double fZwi = pRight->GetYPos(); if(fZwi > fYMax) fYMax = fZwi; if(pList->GetYPos() <= fYMax) { B3dEntity* pTop = pEdgeList->GetStart(); double fXMin = pLeft->GetXPos(); double fXMax = pRight->GetXPos(); if(fXMin > fXMax) { fZwi = fXMin; fXMin = fXMax; fXMax = fZwi; } double fXTop = pTop->GetX(); if(fXMin > fXTop) fXMin = fXTop; if(fXMax < fXTop) fXMax = fXTop; while(pList && pList->GetYPos() <= fYMax) { if(pList->GetXPos() > fXMin && pList->GetXPos() < fXMax) { if(pList->GetStart() != pLeft->GetEnd() && pList->GetStart() != pRight->GetEnd()) { if(IsLeft(pTop, pLeft->GetEnd(), pList->GetStart())) { if(DOUBLE_NOT_EQUAL(pList->GetXPos(), pLeft->GetXPos()) || DOUBLE_NOT_EQUAL(pList->GetYPos(), pLeft->GetYPos())) { if(IsLeft(pRight->GetEnd(), pTop, pList->GetStart())) { if(DOUBLE_NOT_EQUAL(pList->GetXPos(), pRight->GetXPos()) || DOUBLE_NOT_EQUAL(pList->GetYPos(), pRight->GetYPos())) { if(IsLeft(pLeft->GetEnd(), pRight->GetEnd(), pList->GetStart())) { return pList; } } } } } } } // naechste Liste pList = pList->GetDown(); } } } return NULL; } /************************************************************************* |* |* Testen, auf welcher Seite pPoint von der Linie pTop, pDirection liegt |* \************************************************************************/ BOOL B3dComplexPolygon::IsLeft(B3dEntity* pTop, B3dEntity* pDirection, B3dEntity* pPoint) { double fDirX = pDirection->GetX() - pTop->GetX(); double fDirY = pDirection->GetY() - pTop->GetY(); double fPntX = pPoint->GetX() - pTop->GetX(); double fPntY = pPoint->GetY() - pTop->GetY(); return ((fDirX * fPntY - fDirY * fPntX) <= 0.0); } }//end of namespace binfilter // eof /* vim:set shiftwidth=4 softtabstop=4 expandtab: */