/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: b3dgeom.cxx,v $ * * $Revision: 1.7 $ * * last change: $Author: obo $ $Date: 2006-09-17 15:37:52 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_goodies.hxx" #ifndef _B3D_B3DGEOM_HXX #include "b3dgeom.hxx" #endif #ifndef _B3D_B3DCOMPO_HXX #include "b3dcompo.hxx" #endif #ifndef _B3D_HMATRIX_HXX #include "hmatrix.hxx" #endif #ifndef _B3D_BASE3D_HXX #include "base3d.hxx" #endif #ifndef _TOOLS_DEBUG_HXX #include #endif #ifndef _INC_MATH #include #endif /************************************************************************* |* |* Bucket fuer Index |* \************************************************************************/ BASE3D_IMPL_BUCKET(GeometryIndexValue, Bucket) /************************************************************************* |* |* Konstruktor |* \************************************************************************/ B3dGeometry::B3dGeometry() : pComplexPolygon(NULL), aEntityBucket(14), // 16K aIndexBucket(8) // 256Byte { Reset(); } /************************************************************************* |* |* Ausgangszustand der Variablen herstellen |* \************************************************************************/ void B3dGeometry::Reset() { bHintIsComplex = FALSE; if(pComplexPolygon) delete pComplexPolygon; pComplexPolygon = NULL; // #93136# since #92030# uses bOutline flag now as indication // if the filled object is to be drawn, it MUST be initialized now. bOutline = FALSE; } /************************************************************************* |* |* Freien Eintrag zum fuellen holen |* \************************************************************************/ B3dEntity& B3dGeometry::GetFreeEntity() { aEntityBucket.Append(); return aEntityBucket[aEntityBucket.Count() - 1]; } /************************************************************************* |* |* Inhalte loeschen |* \************************************************************************/ void B3dGeometry::Erase() { aEntityBucket.Erase(); aIndexBucket.Erase(); Reset(); } /************************************************************************* |* |* Inhalte loeschen und Speicher freigeben |* \************************************************************************/ void B3dGeometry::Empty() { aEntityBucket.Empty(); aIndexBucket.Empty(); Reset(); } /************************************************************************* |* |* Start der Geometriebeschreibung |* \************************************************************************/ void B3dGeometry::StartDescription() { Erase(); } /************************************************************************* |* |* Ende der Geometriebeschreibung |* \************************************************************************/ void B3dGeometry::EndDescription() { if(pComplexPolygon) delete pComplexPolygon; pComplexPolygon = NULL; } /************************************************************************* |* |* Neues Primitiv beginnen |* \************************************************************************/ void B3dGeometry::StartObject(BOOL bHintComplex, BOOL bOutl) { // Hint uebernehmen bHintIsComplex = bHintComplex; bOutline = bOutl; // ComplexPolygon anlegen falls nicht vorhanden if(bHintIsComplex) { if(!pComplexPolygon) pComplexPolygon = new B3dComplexPolygon; pComplexPolygon->StartPrimitive(); } else { // Direkt neues Polygon beginnen StartPolygon(); } } /************************************************************************* |* |* Primitiv abschliessen |* \************************************************************************/ void B3dGeometry::EndObject() { // Unteren Index holen UINT32 nLow = 0L; if(aIndexBucket.Count()) nLow = aIndexBucket[aIndexBucket.Count()-1].GetIndex(); if(bHintIsComplex) { pComplexPolygon->EndPrimitive(this); } else { // Polygon abschliessen EndPolygon(); } // EbenenNormale berechnen und setzen; bei Linien und // Punkten wird PlaneNormal auf (0,0,0) gesetzt UINT32 nHigh = aIndexBucket[aIndexBucket.Count()-1].GetIndex(); Vector3D aPlaneNormal = -CalcNormal(nLow, nHigh); while(nLow < nHigh) aEntityBucket[nLow++].PlaneNormal() = aPlaneNormal; } /************************************************************************* |* |* Geometrieuebergabe |* \************************************************************************/ void B3dGeometry::AddEdge(const Vector3D& rPoint) { if(bHintIsComplex) { B3dEntity& rNew = pComplexPolygon->GetFreeEntity(); rNew.Reset(); rNew.Point() = rPoint; rNew.SetValid(); rNew.SetEdgeVisible(TRUE); pComplexPolygon->PostAddVertex(rNew); } else { B3dEntity& rNew = GetFreeEntity(); rNew.Reset(); rNew.Point() = rPoint; rNew.SetValid(); rNew.SetEdgeVisible(TRUE); } } void B3dGeometry::AddEdge( const Vector3D& rPoint, const Vector3D& rNormal) { if(bHintIsComplex) { B3dEntity& rNew = pComplexPolygon->GetFreeEntity(); rNew.Reset(); rNew.Point() = rPoint; rNew.SetValid(); rNew.Normal() = rNormal; rNew.SetNormalUsed(); rNew.SetEdgeVisible(TRUE); pComplexPolygon->PostAddVertex(rNew); } else { B3dEntity& rNew = GetFreeEntity(); rNew.Reset(); rNew.Point() = rPoint; rNew.SetValid(); rNew.Normal() = rNormal; rNew.SetNormalUsed(); rNew.SetEdgeVisible(TRUE); } } void B3dGeometry::AddEdge( const Vector3D& rPoint, const Vector3D& rNormal, const Vector3D& rTexture) { if(bHintIsComplex) { B3dEntity& rNew = pComplexPolygon->GetFreeEntity(); rNew.Reset(); rNew.Point() = rPoint; rNew.SetValid(); rNew.Normal() = rNormal; rNew.SetNormalUsed(); rNew.TexCoor() = rTexture; rNew.SetTexCoorUsed(); rNew.SetEdgeVisible(TRUE); pComplexPolygon->PostAddVertex(rNew); } else { B3dEntity& rNew = GetFreeEntity(); rNew.Reset(); rNew.Point() = rPoint; rNew.SetValid(); rNew.Normal() = rNormal; rNew.SetNormalUsed(); rNew.TexCoor() = rTexture; rNew.SetTexCoorUsed(); rNew.SetEdgeVisible(TRUE); } } /************************************************************************* |* |* Copy-Operator |* \************************************************************************/ void B3dGeometry::operator=(const B3dGeometry& rObj) { // Bucket kopieren aEntityBucket = rObj.aEntityBucket; aIndexBucket = rObj.aIndexBucket; // ComplexPolygon nicht kopieren pComplexPolygon = NULL; // Hint auch nicht bHintIsComplex = FALSE; } /************************************************************************* |* |* Callbacks bei komplexen Primitiven |* \************************************************************************/ void B3dGeometry::StartComplexPrimitive() { StartPolygon(); } void B3dGeometry::EndComplexPrimitive() { EndPolygon(); } void B3dGeometry::AddComplexVertex(B3dEntity& rNew, BOOL bIsVisible) { // Kopieren B3dEntity& rLocal = GetFreeEntity(); rLocal = rNew; // EdgeFlag anpassen rLocal.SetEdgeVisible(bIsVisible); } /************************************************************************* |* |* PolygonStart und -Ende aufzeichnen |* \************************************************************************/ void B3dGeometry::StartPolygon() { } void B3dGeometry::EndPolygon() { GeometryIndexValue aNewIndex(aEntityBucket.Count()); if(bOutline) aNewIndex.SetMode(B3D_INDEX_MODE_LINE); aIndexBucket.Append(aNewIndex); } /************************************************************************* |* |* Eine beliebige Transformation auf die Geometrie anwenden |* \************************************************************************/ void B3dGeometry::Transform(const Matrix4D& rMat) { for(UINT32 a=0;aaIndexBucket.Count()) { // get upper bound for new polygon nUpperBound = ((B3dGeometry*)this)->aIndexBucket[nPolyCounter++].GetIndex(); // get cut for that polygon Vector3D aCut; if(CheckSinglePolygonHit(nEntityCounter, nUpperBound, rFront, rBack, aCut)) { rVector.push_back(aCut); } // Auf naechstes Polygon nEntityCounter = nUpperBound; } } sal_Bool B3dGeometry::CheckSinglePolygonHit(UINT32 nLow, UINT32 nHigh, const Vector3D& rFront, const Vector3D& rBack, Vector3D& rCut) const { if(nLow + 2 < nHigh) { // calculate cut with plane if(GetCutPoint(nLow, rCut, rFront, rBack)) { // cut exists, is it inside the polygon? if(IsInside(nLow, nHigh, rCut)) { return sal_True; } } } return sal_False; } sal_Bool B3dGeometry::GetCutPoint(UINT32 nLow, Vector3D& rCut, const Vector3D& rFront, const Vector3D& rBack) const { BOOL bCutValid = FALSE; // Normale und Skalar der Ebenengleichung ermitteln Vector3D aNormal = ((B3dGeometry*)this)->aEntityBucket[nLow].PlaneNormal(); double fScalar = -(((B3dGeometry*)this)->aEntityBucket[nLow + 1].Point().GetVector3D().Scalar(aNormal)); Vector3D aLineVec = rFront - rBack; double fZwi = aNormal.Scalar(aLineVec); if(fabs(fZwi) > SMALL_DVALUE) { fZwi = (-fScalar - (rBack.Scalar(aNormal))) / fZwi; rCut.X() = rBack.X() + (aLineVec.X() * fZwi); rCut.Y() = rBack.Y() + (aLineVec.Y() * fZwi); rCut.Z() = rBack.Z() + (aLineVec.Z() * fZwi); bCutValid = TRUE; } return bCutValid; } sal_Bool B3dGeometry::IsInside(UINT32 nLow, UINT32 nHigh, const Vector3D& rPnt) const { BOOL bInside(FALSE); B3dVolume aVolume; // Volume von genau dieser Flaeche feststellen for(UINT32 a=nLow;aaEntityBucket[a].Point().GetVector3D()); // Hier eigentlich ein aVolume.IsInside(rPnt), doch da hier ein // Vergleich mit Epsilon-Umgebung gebraucht wird, vergleiche selbst BOOL bIsInside = (rPnt.X() + SMALL_DVALUE >= aVolume.MinVec().X() && rPnt.X() - SMALL_DVALUE <= aVolume.MaxVec().X() && rPnt.Y() + SMALL_DVALUE >= aVolume.MinVec().Y() && rPnt.Y() - SMALL_DVALUE <= aVolume.MaxVec().Y() && rPnt.Z() + SMALL_DVALUE >= aVolume.MinVec().Z() && rPnt.Z() - SMALL_DVALUE <= aVolume.MaxVec().Z()); if(bIsInside) { BOOL bInsideXY(FALSE); BOOL bInsideXZ(FALSE); BOOL bInsideYZ(FALSE); const Vector3D* pPrev = &(((B3dGeometry*)this)->aEntityBucket[nHigh - 1].Point().GetVector3D()); const Vector3D* pActual; Vector3D aDiffPrev, aDiffActual; while(nLow < nHigh) { // Neuen Punkt holen pActual = &(((B3dGeometry*)this)->aEntityBucket[nLow++].Point().GetVector3D()); // Diffs bilden aDiffPrev = *pPrev - rPnt; aDiffActual = *pActual - rPnt; // Ueberschneidung in Y moeglich? if((aDiffPrev.Y() > 0.0 && aDiffActual.Y() <= 0.0) || (aDiffActual.Y() > 0.0 && aDiffPrev.Y() <= 0.0)) { // in welchem Bereich liegt X? if(aDiffPrev.X() >= 0.0 && aDiffActual.X() >= 0.0) { // Ueberschneidung bInsideXY = !bInsideXY; } else if((aDiffPrev.X() > 0.0 && aDiffActual.X() <= 0.0) || (aDiffActual.X() > 0.0 && aDiffPrev.X() <= 0.0)) { // eventuell Ueberschneidung // wo liegt die X-Koordinate des Schnitts mit der X-Achse? if(aDiffActual.Y() != aDiffPrev.Y()) if(aDiffPrev.X() - ((aDiffPrev.Y() * (aDiffActual.X() - aDiffPrev.X())) / (aDiffActual.Y() - aDiffPrev.Y())) >= 0.0) // Ueberschneidung bInsideXY = !bInsideXY; } // in welchem Bereich liegt Z? if(aDiffPrev.Z() >= 0.0 && aDiffActual.Z() >= 0.0) { // Ueberschneidung bInsideYZ = !bInsideYZ; } else if((aDiffPrev.Z() > 0.0 && aDiffActual.Z() <= 0.0) || (aDiffActual.Z() > 0.0 && aDiffPrev.Z() <= 0.0)) { // eventuell Ueberschneidung // wo liegt die X-Koordinate des Schnitts mit der X-Achse? if(aDiffActual.Y() != aDiffPrev.Y()) if(aDiffPrev.Z() - ((aDiffPrev.Y() * (aDiffActual.Z() - aDiffPrev.Z())) / (aDiffActual.Y() - aDiffPrev.Y())) >= 0.0) // Ueberschneidung bInsideYZ = !bInsideYZ; } } // Ueberschneidung in X moeglich? if((aDiffPrev.X() > 0.0 && aDiffActual.X() <= 0.0) || (aDiffActual.X() > 0.0 && aDiffPrev.X() <= 0.0)) { // in welchem Bereich liegt Z? if(aDiffPrev.Z() >= 0.0 && aDiffActual.Z() >= 0.0) { // Ueberschneidung bInsideXZ = !bInsideXZ; } else if((aDiffPrev.Z() > 0.0 && aDiffActual.Z() <= 0.0) || (aDiffActual.Z() > 0.0 && aDiffPrev.Z() <= 0.0)) { // eventuell Ueberschneidung // wo liegt die X-Koordinate des Schnitts mit der X-Achse? if(aDiffActual.X() != aDiffPrev.X()) if(aDiffPrev.Z() - ((aDiffPrev.X() * (aDiffActual.Z() - aDiffPrev.Z())) / (aDiffActual.X() - aDiffPrev.X())) >= 0.0) // Ueberschneidung bInsideXZ = !bInsideXZ; } } // Punkt als Vorgaenger merken pPrev = pActual; } // Wahrheitswert bilden bInside = bInsideXY || bInsideXZ || bInsideYZ; } return bInside; } /************************************************************************* |* |* BoundVolume liefern |* \************************************************************************/ B3dVolume B3dGeometry::GetBoundVolume() const { B3dVolume aRetval; for(UINT32 a=0;a<((B3dGeometry*)this)->aEntityBucket.Count();a++) aRetval.Union(((B3dGeometry*)this)->aEntityBucket[a].Point().GetVector3D()); return aRetval; } /************************************************************************* |* |* Mittelpunkt liefern |* \************************************************************************/ Vector3D B3dGeometry::GetCenter() const { B3dVolume aVolume = GetBoundVolume(); return (aVolume.MaxVec() + aVolume.MinVec()) / 2.0; } /************************************************************************* |* |* Standard - Normalen generieren |* \************************************************************************/ void B3dGeometry::CreateDefaultNormalsSphere() { // Alle Normalen ausgehend vom Zentrum der Geometrie bilden Vector3D aCenter = GetCenter(); for(UINT32 a=0;aaEntityBucket[nLow++].Point().GetVector3D()); } else if(!pVec2) { pVec2 = &(((B3dGeometry*)this)->aEntityBucket[nLow++].Point().GetVector3D()); if(*pVec2 == *pVec1) pVec2 = NULL; } else if(!pVec3) { pVec3 = &(((B3dGeometry*)this)->aEntityBucket[nLow++].Point().GetVector3D()); if(*pVec3 == *pVec2 || *pVec3 == *pVec1) // #125865# pVec3 = NULL; } } if(pVec1 && pVec2 && pVec3) { aNormal = (*pVec2 - *pVec1)|(*pVec2 - *pVec3); aNormal.Normalize(); } return aNormal; } /************************************************************************* |* |* Normaleninformationen ungueltig machen |* \************************************************************************/ void B3dGeometry::RemoveNormals() { for(UINT32 a=0;a fXCenter + 0.5) fXPoint -= 1.0; if(fXPoint < fXCenter - 0.5) fXPoint += 1.0; // Polarkoordinaten als Texturkoordinaten zuweisen if(nCreateWhat & B3D_CREATE_DEFAULT_X) aEntityBucket[nPointCounter].TexCoor().X() = fXPoint; if(nCreateWhat & B3D_CREATE_DEFAULT_Y) aEntityBucket[nPointCounter].TexCoor().Y() = fYPoint; if(nCreateWhat & B3D_CREATE_DEFAULT_Z) aEntityBucket[nPointCounter].TexCoor().Z() = 0.0; aEntityBucket[nPointCounter++].SetTexCoorUsed(TRUE); } // Punkte korrigieren, die direkt in den Polarregionen liegen. Deren // X-Koordinate kann nicht korrekt sein. Die korrekte X-Koordinate // ist diejenige des Punktes, der in den Pol hinein oder aus diesem heraus // fuehrt, auf der Kugel also direkt darauf zu. if(nCreateWhat & B3D_CREATE_DEFAULT_X) { nPointCounter = nRememberPointCounter; while(nPointCounter < aIndexBucket[a].GetIndex()) { Vector3D& aCoor = aEntityBucket[nPointCounter].TexCoor(); if(fabs(aCoor.Y()) < SMALL_DVALUE || fabs(aCoor.Y() - 1.0) < SMALL_DVALUE) { // Nachfolger finden UINT32 nNextIndex = (nPointCounter + 1 < aIndexBucket[a].GetIndex()) ? nPointCounter + 1 : nRememberPointCounter; Vector3D& aNextCoor = aEntityBucket[nNextIndex].TexCoor(); // Vorgaenger finden UINT32 nPrevIndex = (nPointCounter && nPointCounter - 1 >= nRememberPointCounter) ? nPointCounter - 1 : aIndexBucket[a].GetIndex() - 1; Vector3D& aPrevCoor = aEntityBucket[nPrevIndex].TexCoor(); // Nachfolger testen: Liegt dieser ausserhalb des Pols? if(fabs(aNextCoor.Y()) > SMALL_DVALUE && fabs(aNextCoor.Y() - 1.0) > SMALL_DVALUE) { // falls ja: X-Koordinate uebernehmen aCoor.X() = aNextCoor.X(); } // Vorgaenger testen: Liegt dieser ausserhalb des Pols? else if(fabs(aPrevCoor.Y()) > SMALL_DVALUE && fabs(aPrevCoor.Y() - 1.0) > SMALL_DVALUE) { // falls ja, X-Koordinate uebernehmen aCoor.X() = aPrevCoor.X(); } else { // Weder Vorgaenger noch Nachfolger liegen ausserhalb des Pols. // Uebernimm daher wenigstens den bereits korrigierten X-Wert // des Vorgaengers aCoor.X() = aPrevCoor.X(); } } // naechster Punkt nPointCounter++; } } } } else { // Texturkoordinaten als Parallelprojektion auf X,Y,Z - Koordinaten // im Bereich 1.0 bis 0.0 der Geometrie abstellen // Gesamtabmessungen holen B3dVolume aVolume = GetBoundVolume(); for(UINT32 a=0;a