blob: b213839850d196fb28955ffadfbda8ad82356e18 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basegfx.hxx"
#include <osl/diagnose.h>
#include <basegfx/polygon/b3dpolygon.hxx>
#include <basegfx/point/b3dpoint.hxx>
#include <basegfx/matrix/b3dhommatrix.hxx>
#include <rtl/instance.hxx>
#include <basegfx/point/b2dpoint.hxx>
#include <basegfx/color/bcolor.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <vector>
#include <algorithm>
//////////////////////////////////////////////////////////////////////////////
class CoordinateData3D
{
basegfx::B3DPoint maPoint;
public:
CoordinateData3D()
: maPoint()
{
}
explicit CoordinateData3D(const basegfx::B3DPoint& rData)
: maPoint(rData)
{
}
const basegfx::B3DPoint& getCoordinate() const
{
return maPoint;
}
void setCoordinate(const basegfx::B3DPoint& rValue)
{
if(rValue != maPoint)
maPoint = rValue;
}
bool operator==(const CoordinateData3D& rData) const
{
return (maPoint == rData.getCoordinate());
}
void transform(const basegfx::B3DHomMatrix& rMatrix)
{
maPoint *= rMatrix;
}
};
//////////////////////////////////////////////////////////////////////////////
class CoordinateDataArray3D
{
typedef ::std::vector< CoordinateData3D > CoordinateData3DVector;
CoordinateData3DVector maVector;
public:
explicit CoordinateDataArray3D(sal_uInt32 nCount)
: maVector(nCount)
{
}
explicit CoordinateDataArray3D(const CoordinateDataArray3D& rOriginal)
: maVector(rOriginal.maVector)
{
}
CoordinateDataArray3D(const CoordinateDataArray3D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
: maVector(rOriginal.maVector.begin() + nIndex, rOriginal.maVector.begin() + (nIndex + nCount))
{
}
~CoordinateDataArray3D()
{
}
::basegfx::B3DVector getNormal() const
{
::basegfx::B3DVector aRetval;
const sal_uInt32 nPointCount(maVector.size());
if(nPointCount > 2)
{
sal_uInt32 nISmallest(0);
sal_uInt32 a(0);
const basegfx::B3DPoint* pSmallest(&maVector[0].getCoordinate());
const basegfx::B3DPoint* pNext(0);
const basegfx::B3DPoint* pPrev(0);
// To guarantee a correctly oriented point, choose an outmost one
// which then cannot be concave
for(a = 1; a < nPointCount; a++)
{
const basegfx::B3DPoint& rCandidate = maVector[a].getCoordinate();
if((rCandidate.getX() < pSmallest->getX())
|| (rCandidate.getX() == pSmallest->getX() && rCandidate.getY() < pSmallest->getY())
|| (rCandidate.getX() == pSmallest->getX() && rCandidate.getY() == pSmallest->getY() && rCandidate.getZ() < pSmallest->getZ()))
{
nISmallest = a;
pSmallest = &rCandidate;
}
}
// look for a next point different from minimal one
for(a = (nISmallest + 1) % nPointCount; a != nISmallest; a = (a + 1) % nPointCount)
{
const basegfx::B3DPoint& rCandidate = maVector[a].getCoordinate();
if(!rCandidate.equal(*pSmallest))
{
pNext = &rCandidate;
break;
}
}
// look for a previous point different from minimal one
for(a = (nISmallest + nPointCount - 1) % nPointCount; a != nISmallest; a = (a + nPointCount - 1) % nPointCount)
{
const basegfx::B3DPoint& rCandidate = maVector[a].getCoordinate();
if(!rCandidate.equal(*pSmallest))
{
pPrev = &rCandidate;
break;
}
}
// we always have a minimal point. If we also have a different next and previous,
// we can calculate the normal
if(pNext && pPrev)
{
const basegfx::B3DVector aPrev(*pPrev - *pSmallest);
const basegfx::B3DVector aNext(*pNext - *pSmallest);
aRetval = cross(aPrev, aNext);
aRetval.normalize();
}
}
return aRetval;
}
sal_uInt32 count() const
{
return maVector.size();
}
bool operator==(const CoordinateDataArray3D& rCandidate) const
{
return (maVector == rCandidate.maVector);
}
const basegfx::B3DPoint& getCoordinate(sal_uInt32 nIndex) const
{
return maVector[nIndex].getCoordinate();
}
void setCoordinate(sal_uInt32 nIndex, const basegfx::B3DPoint& rValue)
{
maVector[nIndex].setCoordinate(rValue);
}
void insert(sal_uInt32 nIndex, const CoordinateData3D& rValue, sal_uInt32 nCount)
{
if(nCount)
{
// add nCount copies of rValue
CoordinateData3DVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
maVector.insert(aIndex, nCount, rValue);
}
}
void insert(sal_uInt32 nIndex, const CoordinateDataArray3D& rSource)
{
const sal_uInt32 nCount(rSource.maVector.size());
if(nCount)
{
// insert data
CoordinateData3DVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
CoordinateData3DVector::const_iterator aStart(rSource.maVector.begin());
CoordinateData3DVector::const_iterator aEnd(rSource.maVector.end());
maVector.insert(aIndex, aStart, aEnd);
}
}
void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
if(nCount)
{
// remove point data
CoordinateData3DVector::iterator aStart(maVector.begin());
aStart += nIndex;
const CoordinateData3DVector::iterator aEnd(aStart + nCount);
maVector.erase(aStart, aEnd);
}
}
void flip()
{
if(maVector.size() > 1)
{
const sal_uInt32 nHalfSize(maVector.size() >> 1L);
CoordinateData3DVector::iterator aStart(maVector.begin());
CoordinateData3DVector::iterator aEnd(maVector.end() - 1L);
for(sal_uInt32 a(0); a < nHalfSize; a++)
{
::std::swap(*aStart, *aEnd);
aStart++;
aEnd--;
}
}
}
void transform(const ::basegfx::B3DHomMatrix& rMatrix)
{
CoordinateData3DVector::iterator aStart(maVector.begin());
CoordinateData3DVector::iterator aEnd(maVector.end());
for(; aStart != aEnd; aStart++)
{
aStart->transform(rMatrix);
}
}
};
//////////////////////////////////////////////////////////////////////////////
class BColorArray
{
typedef ::std::vector< ::basegfx::BColor > BColorDataVector;
BColorDataVector maVector;
sal_uInt32 mnUsedEntries;
public:
explicit BColorArray(sal_uInt32 nCount)
: maVector(nCount),
mnUsedEntries(0L)
{
}
explicit BColorArray(const BColorArray& rOriginal)
: maVector(rOriginal.maVector),
mnUsedEntries(rOriginal.mnUsedEntries)
{
}
BColorArray(const BColorArray& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
: maVector(),
mnUsedEntries(0L)
{
BColorDataVector::const_iterator aStart(rOriginal.maVector.begin());
aStart += nIndex;
BColorDataVector::const_iterator aEnd(aStart);
aEnd += nCount;
maVector.reserve(nCount);
for(; aStart != aEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries++;
maVector.push_back(*aStart);
}
}
~BColorArray()
{
}
sal_uInt32 count() const
{
return maVector.size();
}
bool operator==(const BColorArray& rCandidate) const
{
return (maVector == rCandidate.maVector);
}
bool isUsed() const
{
return (0L != mnUsedEntries);
}
const ::basegfx::BColor& getBColor(sal_uInt32 nIndex) const
{
return maVector[nIndex];
}
void setBColor(sal_uInt32 nIndex, const ::basegfx::BColor& rValue)
{
bool bWasUsed(mnUsedEntries && !maVector[nIndex].equalZero());
bool bIsUsed(!rValue.equalZero());
if(bWasUsed)
{
if(bIsUsed)
{
maVector[nIndex] = rValue;
}
else
{
maVector[nIndex] = ::basegfx::BColor::getEmptyBColor();
mnUsedEntries--;
}
}
else
{
if(bIsUsed)
{
maVector[nIndex] = rValue;
mnUsedEntries++;
}
}
}
void insert(sal_uInt32 nIndex, const ::basegfx::BColor& rValue, sal_uInt32 nCount)
{
if(nCount)
{
// add nCount copies of rValue
BColorDataVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
maVector.insert(aIndex, nCount, rValue);
if(!rValue.equalZero())
mnUsedEntries += nCount;
}
}
void insert(sal_uInt32 nIndex, const BColorArray& rSource)
{
const sal_uInt32 nCount(rSource.maVector.size());
if(nCount)
{
// insert data
BColorDataVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
BColorDataVector::const_iterator aStart(rSource.maVector.begin());
BColorDataVector::const_iterator aEnd(rSource.maVector.end());
maVector.insert(aIndex, aStart, aEnd);
for(; aStart != aEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries++;
}
}
}
void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
if(nCount)
{
const BColorDataVector::iterator aDeleteStart(maVector.begin() + nIndex);
const BColorDataVector::iterator aDeleteEnd(aDeleteStart + nCount);
BColorDataVector::const_iterator aStart(aDeleteStart);
for(; mnUsedEntries && aStart != aDeleteEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries--;
}
// remove point data
maVector.erase(aDeleteStart, aDeleteEnd);
}
}
void flip()
{
if(maVector.size() > 1)
{
const sal_uInt32 nHalfSize(maVector.size() >> 1L);
BColorDataVector::iterator aStart(maVector.begin());
BColorDataVector::iterator aEnd(maVector.end() - 1L);
for(sal_uInt32 a(0); a < nHalfSize; a++)
{
::std::swap(*aStart, *aEnd);
aStart++;
aEnd--;
}
}
}
};
//////////////////////////////////////////////////////////////////////////////
class NormalsArray3D
{
typedef ::std::vector< ::basegfx::B3DVector > NormalsData3DVector;
NormalsData3DVector maVector;
sal_uInt32 mnUsedEntries;
public:
explicit NormalsArray3D(sal_uInt32 nCount)
: maVector(nCount),
mnUsedEntries(0L)
{
}
explicit NormalsArray3D(const NormalsArray3D& rOriginal)
: maVector(rOriginal.maVector),
mnUsedEntries(rOriginal.mnUsedEntries)
{
}
NormalsArray3D(const NormalsArray3D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
: maVector(),
mnUsedEntries(0L)
{
NormalsData3DVector::const_iterator aStart(rOriginal.maVector.begin());
aStart += nIndex;
NormalsData3DVector::const_iterator aEnd(aStart);
aEnd += nCount;
maVector.reserve(nCount);
for(; aStart != aEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries++;
maVector.push_back(*aStart);
}
}
~NormalsArray3D()
{
}
sal_uInt32 count() const
{
return maVector.size();
}
bool operator==(const NormalsArray3D& rCandidate) const
{
return (maVector == rCandidate.maVector);
}
bool isUsed() const
{
return (0L != mnUsedEntries);
}
const ::basegfx::B3DVector& getNormal(sal_uInt32 nIndex) const
{
return maVector[nIndex];
}
void setNormal(sal_uInt32 nIndex, const ::basegfx::B3DVector& rValue)
{
bool bWasUsed(mnUsedEntries && !maVector[nIndex].equalZero());
bool bIsUsed(!rValue.equalZero());
if(bWasUsed)
{
if(bIsUsed)
{
maVector[nIndex] = rValue;
}
else
{
maVector[nIndex] = ::basegfx::B3DVector::getEmptyVector();
mnUsedEntries--;
}
}
else
{
if(bIsUsed)
{
maVector[nIndex] = rValue;
mnUsedEntries++;
}
}
}
void insert(sal_uInt32 nIndex, const ::basegfx::B3DVector& rValue, sal_uInt32 nCount)
{
if(nCount)
{
// add nCount copies of rValue
NormalsData3DVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
maVector.insert(aIndex, nCount, rValue);
if(!rValue.equalZero())
mnUsedEntries += nCount;
}
}
void insert(sal_uInt32 nIndex, const NormalsArray3D& rSource)
{
const sal_uInt32 nCount(rSource.maVector.size());
if(nCount)
{
// insert data
NormalsData3DVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
NormalsData3DVector::const_iterator aStart(rSource.maVector.begin());
NormalsData3DVector::const_iterator aEnd(rSource.maVector.end());
maVector.insert(aIndex, aStart, aEnd);
for(; aStart != aEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries++;
}
}
}
void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
if(nCount)
{
const NormalsData3DVector::iterator aDeleteStart(maVector.begin() + nIndex);
const NormalsData3DVector::iterator aDeleteEnd(aDeleteStart + nCount);
NormalsData3DVector::const_iterator aStart(aDeleteStart);
for(; mnUsedEntries && aStart != aDeleteEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries--;
}
// remove point data
maVector.erase(aDeleteStart, aDeleteEnd);
}
}
void flip()
{
if(maVector.size() > 1)
{
const sal_uInt32 nHalfSize(maVector.size() >> 1L);
NormalsData3DVector::iterator aStart(maVector.begin());
NormalsData3DVector::iterator aEnd(maVector.end() - 1L);
for(sal_uInt32 a(0); a < nHalfSize; a++)
{
::std::swap(*aStart, *aEnd);
aStart++;
aEnd--;
}
}
}
void transform(const basegfx::B3DHomMatrix& rMatrix)
{
NormalsData3DVector::iterator aStart(maVector.begin());
NormalsData3DVector::iterator aEnd(maVector.end());
for(; aStart != aEnd; aStart++)
{
(*aStart) *= rMatrix;
}
}
};
//////////////////////////////////////////////////////////////////////////////
class TextureCoordinate2D
{
typedef ::std::vector< ::basegfx::B2DPoint > TextureData2DVector;
TextureData2DVector maVector;
sal_uInt32 mnUsedEntries;
public:
explicit TextureCoordinate2D(sal_uInt32 nCount)
: maVector(nCount),
mnUsedEntries(0L)
{
}
explicit TextureCoordinate2D(const TextureCoordinate2D& rOriginal)
: maVector(rOriginal.maVector),
mnUsedEntries(rOriginal.mnUsedEntries)
{
}
TextureCoordinate2D(const TextureCoordinate2D& rOriginal, sal_uInt32 nIndex, sal_uInt32 nCount)
: maVector(),
mnUsedEntries(0L)
{
TextureData2DVector::const_iterator aStart(rOriginal.maVector.begin());
aStart += nIndex;
TextureData2DVector::const_iterator aEnd(aStart);
aEnd += nCount;
maVector.reserve(nCount);
for(; aStart != aEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries++;
maVector.push_back(*aStart);
}
}
~TextureCoordinate2D()
{
}
sal_uInt32 count() const
{
return maVector.size();
}
bool operator==(const TextureCoordinate2D& rCandidate) const
{
return (maVector == rCandidate.maVector);
}
bool isUsed() const
{
return (0L != mnUsedEntries);
}
const ::basegfx::B2DPoint& getTextureCoordinate(sal_uInt32 nIndex) const
{
return maVector[nIndex];
}
void setTextureCoordinate(sal_uInt32 nIndex, const ::basegfx::B2DPoint& rValue)
{
bool bWasUsed(mnUsedEntries && !maVector[nIndex].equalZero());
bool bIsUsed(!rValue.equalZero());
if(bWasUsed)
{
if(bIsUsed)
{
maVector[nIndex] = rValue;
}
else
{
maVector[nIndex] = ::basegfx::B2DPoint::getEmptyPoint();
mnUsedEntries--;
}
}
else
{
if(bIsUsed)
{
maVector[nIndex] = rValue;
mnUsedEntries++;
}
}
}
void insert(sal_uInt32 nIndex, const ::basegfx::B2DPoint& rValue, sal_uInt32 nCount)
{
if(nCount)
{
// add nCount copies of rValue
TextureData2DVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
maVector.insert(aIndex, nCount, rValue);
if(!rValue.equalZero())
mnUsedEntries += nCount;
}
}
void insert(sal_uInt32 nIndex, const TextureCoordinate2D& rSource)
{
const sal_uInt32 nCount(rSource.maVector.size());
if(nCount)
{
// insert data
TextureData2DVector::iterator aIndex(maVector.begin());
aIndex += nIndex;
TextureData2DVector::const_iterator aStart(rSource.maVector.begin());
TextureData2DVector::const_iterator aEnd(rSource.maVector.end());
maVector.insert(aIndex, aStart, aEnd);
for(; aStart != aEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries++;
}
}
}
void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
if(nCount)
{
const TextureData2DVector::iterator aDeleteStart(maVector.begin() + nIndex);
const TextureData2DVector::iterator aDeleteEnd(aDeleteStart + nCount);
TextureData2DVector::const_iterator aStart(aDeleteStart);
for(; mnUsedEntries && aStart != aDeleteEnd; aStart++)
{
if(!aStart->equalZero())
mnUsedEntries--;
}
// remove point data
maVector.erase(aDeleteStart, aDeleteEnd);
}
}
void flip()
{
if(maVector.size() > 1)
{
const sal_uInt32 nHalfSize(maVector.size() >> 1L);
TextureData2DVector::iterator aStart(maVector.begin());
TextureData2DVector::iterator aEnd(maVector.end() - 1L);
for(sal_uInt32 a(0); a < nHalfSize; a++)
{
::std::swap(*aStart, *aEnd);
aStart++;
aEnd--;
}
}
}
void transform(const ::basegfx::B2DHomMatrix& rMatrix)
{
TextureData2DVector::iterator aStart(maVector.begin());
TextureData2DVector::iterator aEnd(maVector.end());
for(; aStart != aEnd; aStart++)
{
(*aStart) *= rMatrix;
}
}
};
//////////////////////////////////////////////////////////////////////////////
class ImplB3DPolygon
{
// The point vector. This vector exists always and defines the
// count of members.
CoordinateDataArray3D maPoints;
// The BColor vector. This vectors are created on demand
// and may be zero.
BColorArray* mpBColors;
// The Normals vector. This vectors are created on demand
// and may be zero.
NormalsArray3D* mpNormals;
// The TextureCoordinates vector. This vectors are created on demand
// and may be zero.
TextureCoordinate2D* mpTextureCoordiantes;
// The calculated plane normal. mbPlaneNormalValid says if it's valid.
::basegfx::B3DVector maPlaneNormal;
// bitfield
// flag which decides if this polygon is opened or closed
unsigned mbIsClosed : 1;
// flag which says if maPlaneNormal is up-to-date
unsigned mbPlaneNormalValid : 1;
protected:
void invalidatePlaneNormal()
{
if(mbPlaneNormalValid)
{
mbPlaneNormalValid = false;
}
}
public:
// This constructor is only used from the static identity polygon, thus
// the RefCount is set to 1 to never 'delete' this static incarnation.
ImplB3DPolygon()
: maPoints(0L),
mpBColors(0L),
mpNormals(0L),
mpTextureCoordiantes(0L),
maPlaneNormal(::basegfx::B3DVector::getEmptyVector()),
mbIsClosed(false),
mbPlaneNormalValid(true)
{
// complete initialization with defaults
}
ImplB3DPolygon(const ImplB3DPolygon& rToBeCopied)
: maPoints(rToBeCopied.maPoints),
mpBColors(0L),
mpNormals(0L),
mpTextureCoordiantes(0L),
maPlaneNormal(rToBeCopied.maPlaneNormal),
mbIsClosed(rToBeCopied.mbIsClosed),
mbPlaneNormalValid(rToBeCopied.mbPlaneNormalValid)
{
// complete initialization using copy
if(rToBeCopied.mpBColors && rToBeCopied.mpBColors->isUsed())
{
mpBColors = new BColorArray(*rToBeCopied.mpBColors);
}
if(rToBeCopied.mpNormals && rToBeCopied.mpNormals->isUsed())
{
mpNormals = new NormalsArray3D(*rToBeCopied.mpNormals);
}
if(rToBeCopied.mpTextureCoordiantes && rToBeCopied.mpTextureCoordiantes->isUsed())
{
mpTextureCoordiantes = new TextureCoordinate2D(*rToBeCopied.mpTextureCoordiantes);
}
}
ImplB3DPolygon(const ImplB3DPolygon& rToBeCopied, sal_uInt32 nIndex, sal_uInt32 nCount)
: maPoints(rToBeCopied.maPoints, nIndex, nCount),
mpBColors(0L),
mpNormals(0L),
mpTextureCoordiantes(0L),
maPlaneNormal(::basegfx::B3DVector::getEmptyVector()),
mbIsClosed(rToBeCopied.mbIsClosed),
mbPlaneNormalValid(false)
{
// complete initialization using partly copy
if(rToBeCopied.mpBColors && rToBeCopied.mpBColors->isUsed())
{
mpBColors = new BColorArray(*rToBeCopied.mpBColors, nIndex, nCount);
if(!mpBColors->isUsed())
{
delete mpBColors;
mpBColors = 0L;
}
}
if(rToBeCopied.mpNormals && rToBeCopied.mpNormals->isUsed())
{
mpNormals = new NormalsArray3D(*rToBeCopied.mpNormals, nIndex, nCount);
if(!mpNormals->isUsed())
{
delete mpNormals;
mpNormals = 0L;
}
}
if(rToBeCopied.mpTextureCoordiantes && rToBeCopied.mpTextureCoordiantes->isUsed())
{
mpTextureCoordiantes = new TextureCoordinate2D(*rToBeCopied.mpTextureCoordiantes, nIndex, nCount);
if(!mpTextureCoordiantes->isUsed())
{
delete mpTextureCoordiantes;
mpTextureCoordiantes = 0L;
}
}
}
~ImplB3DPolygon()
{
if(mpBColors)
{
delete mpBColors;
mpBColors = 0L;
}
if(mpNormals)
{
delete mpNormals;
mpNormals = 0L;
}
if(mpTextureCoordiantes)
{
delete mpTextureCoordiantes;
mpTextureCoordiantes = 0L;
}
}
sal_uInt32 count() const
{
return maPoints.count();
}
bool isClosed() const
{
return mbIsClosed;
}
void setClosed(bool bNew)
{
if(bNew != (bool)mbIsClosed)
{
mbIsClosed = bNew;
}
}
inline bool impBColorsAreEqual(const ImplB3DPolygon& rCandidate) const
{
bool bBColorsAreEqual(true);
if(mpBColors)
{
if(rCandidate.mpBColors)
{
bBColorsAreEqual = (*mpBColors == *rCandidate.mpBColors);
}
else
{
// candidate has no BColors, so it's assumed all unused.
bBColorsAreEqual = !mpBColors->isUsed();
}
}
else
{
if(rCandidate.mpBColors)
{
// we have no TextureCoordiantes, so it's assumed all unused.
bBColorsAreEqual = !rCandidate.mpBColors->isUsed();
}
}
return bBColorsAreEqual;
}
inline bool impNormalsAreEqual(const ImplB3DPolygon& rCandidate) const
{
bool bNormalsAreEqual(true);
if(mpNormals)
{
if(rCandidate.mpNormals)
{
bNormalsAreEqual = (*mpNormals == *rCandidate.mpNormals);
}
else
{
// candidate has no normals, so it's assumed all unused.
bNormalsAreEqual = !mpNormals->isUsed();
}
}
else
{
if(rCandidate.mpNormals)
{
// we have no normals, so it's assumed all unused.
bNormalsAreEqual = !rCandidate.mpNormals->isUsed();
}
}
return bNormalsAreEqual;
}
inline bool impTextureCoordinatesAreEqual(const ImplB3DPolygon& rCandidate) const
{
bool bTextureCoordinatesAreEqual(true);
if(mpTextureCoordiantes)
{
if(rCandidate.mpTextureCoordiantes)
{
bTextureCoordinatesAreEqual = (*mpTextureCoordiantes == *rCandidate.mpTextureCoordiantes);
}
else
{
// candidate has no TextureCoordinates, so it's assumed all unused.
bTextureCoordinatesAreEqual = !mpTextureCoordiantes->isUsed();
}
}
else
{
if(rCandidate.mpTextureCoordiantes)
{
// we have no TextureCoordiantes, so it's assumed all unused.
bTextureCoordinatesAreEqual = !rCandidate.mpTextureCoordiantes->isUsed();
}
}
return bTextureCoordinatesAreEqual;
}
bool operator==(const ImplB3DPolygon& rCandidate) const
{
if(mbIsClosed == rCandidate.mbIsClosed)
{
if(maPoints == rCandidate.maPoints)
{
if(impBColorsAreEqual(rCandidate))
{
if(impNormalsAreEqual(rCandidate))
{
if(impTextureCoordinatesAreEqual(rCandidate))
{
return true;
}
}
}
}
}
return false;
}
const ::basegfx::B3DPoint& getPoint(sal_uInt32 nIndex) const
{
return maPoints.getCoordinate(nIndex);
}
void setPoint(sal_uInt32 nIndex, const ::basegfx::B3DPoint& rValue)
{
maPoints.setCoordinate(nIndex, rValue);
invalidatePlaneNormal();
}
void insert(sal_uInt32 nIndex, const ::basegfx::B3DPoint& rPoint, sal_uInt32 nCount)
{
if(nCount)
{
CoordinateData3D aCoordinate(rPoint);
maPoints.insert(nIndex, aCoordinate, nCount);
invalidatePlaneNormal();
if(mpBColors)
{
mpBColors->insert(nIndex, ::basegfx::BColor::getEmptyBColor(), nCount);
}
if(mpNormals)
{
mpNormals->insert(nIndex, ::basegfx::B3DVector::getEmptyVector(), nCount);
}
if(mpTextureCoordiantes)
{
mpTextureCoordiantes->insert(nIndex, ::basegfx::B2DPoint::getEmptyPoint(), nCount);
}
}
}
const ::basegfx::BColor& getBColor(sal_uInt32 nIndex) const
{
if(mpBColors)
{
return mpBColors->getBColor(nIndex);
}
else
{
return ::basegfx::BColor::getEmptyBColor();
}
}
void setBColor(sal_uInt32 nIndex, const ::basegfx::BColor& rValue)
{
if(!mpBColors)
{
if(!rValue.equalZero())
{
mpBColors = new BColorArray(maPoints.count());
mpBColors->setBColor(nIndex, rValue);
}
}
else
{
mpBColors->setBColor(nIndex, rValue);
if(!mpBColors->isUsed())
{
delete mpBColors;
mpBColors = 0L;
}
}
}
bool areBColorsUsed() const
{
return (mpBColors && mpBColors->isUsed());
}
void clearBColors()
{
if(mpBColors)
{
delete mpBColors;
mpBColors = 0L;
}
}
const ::basegfx::B3DVector& getNormal() const
{
if(!mbPlaneNormalValid)
{
const_cast< ImplB3DPolygon* >(this)->maPlaneNormal = maPoints.getNormal();
const_cast< ImplB3DPolygon* >(this)->mbPlaneNormalValid = true;
}
return maPlaneNormal;
}
const ::basegfx::B3DVector& getNormal(sal_uInt32 nIndex) const
{
if(mpNormals)
{
return mpNormals->getNormal(nIndex);
}
else
{
return ::basegfx::B3DVector::getEmptyVector();
}
}
void setNormal(sal_uInt32 nIndex, const ::basegfx::B3DVector& rValue)
{
if(!mpNormals)
{
if(!rValue.equalZero())
{
mpNormals = new NormalsArray3D(maPoints.count());
mpNormals->setNormal(nIndex, rValue);
}
}
else
{
mpNormals->setNormal(nIndex, rValue);
if(!mpNormals->isUsed())
{
delete mpNormals;
mpNormals = 0L;
}
}
}
void transformNormals(const ::basegfx::B3DHomMatrix& rMatrix)
{
if(mpNormals)
{
mpNormals->transform(rMatrix);
}
}
bool areNormalsUsed() const
{
return (mpNormals && mpNormals->isUsed());
}
void clearNormals()
{
if(mpNormals)
{
delete mpNormals;
mpNormals = 0L;
}
}
const ::basegfx::B2DPoint& getTextureCoordinate(sal_uInt32 nIndex) const
{
if(mpTextureCoordiantes)
{
return mpTextureCoordiantes->getTextureCoordinate(nIndex);
}
else
{
return ::basegfx::B2DPoint::getEmptyPoint();
}
}
void setTextureCoordinate(sal_uInt32 nIndex, const ::basegfx::B2DPoint& rValue)
{
if(!mpTextureCoordiantes)
{
if(!rValue.equalZero())
{
mpTextureCoordiantes = new TextureCoordinate2D(maPoints.count());
mpTextureCoordiantes->setTextureCoordinate(nIndex, rValue);
}
}
else
{
mpTextureCoordiantes->setTextureCoordinate(nIndex, rValue);
if(!mpTextureCoordiantes->isUsed())
{
delete mpTextureCoordiantes;
mpTextureCoordiantes = 0L;
}
}
}
bool areTextureCoordinatesUsed() const
{
return (mpTextureCoordiantes && mpTextureCoordiantes->isUsed());
}
void clearTextureCoordinates()
{
if(mpTextureCoordiantes)
{
delete mpTextureCoordiantes;
mpTextureCoordiantes = 0L;
}
}
void transformTextureCoordinates(const ::basegfx::B2DHomMatrix& rMatrix)
{
if(mpTextureCoordiantes)
{
mpTextureCoordiantes->transform(rMatrix);
}
}
void insert(sal_uInt32 nIndex, const ImplB3DPolygon& rSource)
{
const sal_uInt32 nCount(rSource.maPoints.count());
if(nCount)
{
maPoints.insert(nIndex, rSource.maPoints);
invalidatePlaneNormal();
if(rSource.mpBColors && rSource.mpBColors->isUsed())
{
if(!mpBColors)
{
mpBColors = new BColorArray(maPoints.count());
}
mpBColors->insert(nIndex, *rSource.mpBColors);
}
else
{
if(mpBColors)
{
mpBColors->insert(nIndex, ::basegfx::BColor::getEmptyBColor(), nCount);
}
}
if(rSource.mpNormals && rSource.mpNormals->isUsed())
{
if(!mpNormals)
{
mpNormals = new NormalsArray3D(maPoints.count());
}
mpNormals->insert(nIndex, *rSource.mpNormals);
}
else
{
if(mpNormals)
{
mpNormals->insert(nIndex, ::basegfx::B3DVector::getEmptyVector(), nCount);
}
}
if(rSource.mpTextureCoordiantes && rSource.mpTextureCoordiantes->isUsed())
{
if(!mpTextureCoordiantes)
{
mpTextureCoordiantes = new TextureCoordinate2D(maPoints.count());
}
mpTextureCoordiantes->insert(nIndex, *rSource.mpTextureCoordiantes);
}
else
{
if(mpTextureCoordiantes)
{
mpTextureCoordiantes->insert(nIndex, ::basegfx::B2DPoint::getEmptyPoint(), nCount);
}
}
}
}
void remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
if(nCount)
{
maPoints.remove(nIndex, nCount);
invalidatePlaneNormal();
if(mpBColors)
{
mpBColors->remove(nIndex, nCount);
if(!mpBColors->isUsed())
{
delete mpBColors;
mpBColors = 0L;
}
}
if(mpNormals)
{
mpNormals->remove(nIndex, nCount);
if(!mpNormals->isUsed())
{
delete mpNormals;
mpNormals = 0L;
}
}
if(mpTextureCoordiantes)
{
mpTextureCoordiantes->remove(nIndex, nCount);
if(!mpTextureCoordiantes->isUsed())
{
delete mpTextureCoordiantes;
mpTextureCoordiantes = 0L;
}
}
}
}
void flip()
{
if(maPoints.count() > 1)
{
maPoints.flip();
if(mbPlaneNormalValid)
{
// mirror plane normal
maPlaneNormal = -maPlaneNormal;
}
if(mpBColors)
{
mpBColors->flip();
}
if(mpNormals)
{
mpNormals->flip();
}
if(mpTextureCoordiantes)
{
mpTextureCoordiantes->flip();
}
}
}
bool hasDoublePoints() const
{
if(mbIsClosed)
{
// check for same start and end point
const sal_uInt32 nIndex(maPoints.count() - 1L);
if(maPoints.getCoordinate(0L) == maPoints.getCoordinate(nIndex))
{
const bool bBColorEqual(!mpBColors || (mpBColors->getBColor(0L) == mpBColors->getBColor(nIndex)));
if(bBColorEqual)
{
const bool bNormalsEqual(!mpNormals || (mpNormals->getNormal(0L) == mpNormals->getNormal(nIndex)));
if(bNormalsEqual)
{
const bool bTextureCoordinatesEqual(!mpTextureCoordiantes || (mpTextureCoordiantes->getTextureCoordinate(0L) == mpTextureCoordiantes->getTextureCoordinate(nIndex)));
if(bTextureCoordinatesEqual)
{
return true;
}
}
}
}
}
// test for range
for(sal_uInt32 a(0L); a < maPoints.count() - 1L; a++)
{
if(maPoints.getCoordinate(a) == maPoints.getCoordinate(a + 1L))
{
const bool bBColorEqual(!mpBColors || (mpBColors->getBColor(a) == mpBColors->getBColor(a + 1L)));
if(bBColorEqual)
{
const bool bNormalsEqual(!mpNormals || (mpNormals->getNormal(a) == mpNormals->getNormal(a + 1L)));
if(bNormalsEqual)
{
const bool bTextureCoordinatesEqual(!mpTextureCoordiantes || (mpTextureCoordiantes->getTextureCoordinate(a) == mpTextureCoordiantes->getTextureCoordinate(a + 1L)));
if(bTextureCoordinatesEqual)
{
return true;
}
}
}
}
}
return false;
}
void removeDoublePointsAtBeginEnd()
{
// Only remove DoublePoints at Begin and End when poly is closed
if(mbIsClosed)
{
bool bRemove;
do
{
bRemove = false;
if(maPoints.count() > 1L)
{
const sal_uInt32 nIndex(maPoints.count() - 1L);
bRemove = (maPoints.getCoordinate(0L) == maPoints.getCoordinate(nIndex));
if(bRemove && mpBColors && !(mpBColors->getBColor(0L) == mpBColors->getBColor(nIndex)))
{
bRemove = false;
}
if(bRemove && mpNormals && !(mpNormals->getNormal(0L) == mpNormals->getNormal(nIndex)))
{
bRemove = false;
}
if(bRemove && mpTextureCoordiantes && !(mpTextureCoordiantes->getTextureCoordinate(0L) == mpTextureCoordiantes->getTextureCoordinate(nIndex)))
{
bRemove = false;
}
}
if(bRemove)
{
const sal_uInt32 nIndex(maPoints.count() - 1L);
remove(nIndex, 1L);
}
} while(bRemove);
}
}
void removeDoublePointsWholeTrack()
{
sal_uInt32 nIndex(0L);
// test as long as there are at least two points and as long as the index
// is smaller or equal second last point
while((maPoints.count() > 1L) && (nIndex <= maPoints.count() - 2L))
{
const sal_uInt32 nNextIndex(nIndex + 1L);
bool bRemove(maPoints.getCoordinate(nIndex) == maPoints.getCoordinate(nNextIndex));
if(bRemove && mpBColors && !(mpBColors->getBColor(nIndex) == mpBColors->getBColor(nNextIndex)))
{
bRemove = false;
}
if(bRemove && mpNormals && !(mpNormals->getNormal(nIndex) == mpNormals->getNormal(nNextIndex)))
{
bRemove = false;
}
if(bRemove && mpTextureCoordiantes && !(mpTextureCoordiantes->getTextureCoordinate(nIndex) == mpTextureCoordiantes->getTextureCoordinate(nNextIndex)))
{
bRemove = false;
}
if(bRemove)
{
// if next is same as index and the control vectors are unused, delete index
remove(nIndex, 1L);
}
else
{
// if different, step forward
nIndex++;
}
}
}
void transform(const ::basegfx::B3DHomMatrix& rMatrix)
{
maPoints.transform(rMatrix);
// Here, it seems to be possible to transform a valid plane normal and to avoid
// invalidation, but it's not true. If the transformation contains shears or e.g.
// perspective projection, the orthogonality to the transformed plane will not
// be preserved. It may be possible to test that at the matrix to not invalidate in
// all cases or to extract a matrix which does not 'shear' the vector which is
// a normal in this case. As long as this is not sure, i will just invalidate.
invalidatePlaneNormal();
}
};
//////////////////////////////////////////////////////////////////////////////
namespace basegfx
{
namespace { struct DefaultPolygon : public rtl::Static< B3DPolygon::ImplType,
DefaultPolygon > {}; }
B3DPolygon::B3DPolygon() :
mpPolygon(DefaultPolygon::get())
{
}
B3DPolygon::B3DPolygon(const B3DPolygon& rPolygon) :
mpPolygon(rPolygon.mpPolygon)
{
}
B3DPolygon::B3DPolygon(const B3DPolygon& rPolygon, sal_uInt32 nIndex, sal_uInt32 nCount) :
mpPolygon(ImplB3DPolygon(*rPolygon.mpPolygon, nIndex, nCount))
{
// TODO(P2): one extra temporary here (cow_wrapper copies
// given ImplB3DPolygon into its internal impl_t wrapper type)
OSL_ENSURE(nIndex + nCount > rPolygon.mpPolygon->count(), "B3DPolygon constructor outside range (!)");
}
B3DPolygon::~B3DPolygon()
{
}
B3DPolygon& B3DPolygon::operator=(const B3DPolygon& rPolygon)
{
mpPolygon = rPolygon.mpPolygon;
return *this;
}
void B3DPolygon::makeUnique()
{
mpPolygon.make_unique();
}
bool B3DPolygon::operator==(const B3DPolygon& rPolygon) const
{
if(mpPolygon.same_object(rPolygon.mpPolygon))
return true;
return (*mpPolygon == *rPolygon.mpPolygon);
}
bool B3DPolygon::operator!=(const B3DPolygon& rPolygon) const
{
return !(*this == rPolygon);
}
sal_uInt32 B3DPolygon::count() const
{
return mpPolygon->count();
}
basegfx::B3DPoint B3DPolygon::getB3DPoint(sal_uInt32 nIndex) const
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
return mpPolygon->getPoint(nIndex);
}
void B3DPolygon::setB3DPoint(sal_uInt32 nIndex, const basegfx::B3DPoint& rValue)
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
if(getB3DPoint(nIndex) != rValue)
mpPolygon->setPoint(nIndex, rValue);
}
BColor B3DPolygon::getBColor(sal_uInt32 nIndex) const
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
return mpPolygon->getBColor(nIndex);
}
void B3DPolygon::setBColor(sal_uInt32 nIndex, const BColor& rValue)
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
if(mpPolygon->getBColor(nIndex) != rValue)
mpPolygon->setBColor(nIndex, rValue);
}
bool B3DPolygon::areBColorsUsed() const
{
return mpPolygon->areBColorsUsed();
}
void B3DPolygon::clearBColors()
{
if(mpPolygon->areBColorsUsed())
mpPolygon->clearBColors();
}
B3DVector B3DPolygon::getNormal() const
{
return mpPolygon->getNormal();
}
B3DVector B3DPolygon::getNormal(sal_uInt32 nIndex) const
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
return mpPolygon->getNormal(nIndex);
}
void B3DPolygon::setNormal(sal_uInt32 nIndex, const B3DVector& rValue)
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
if(mpPolygon->getNormal(nIndex) != rValue)
mpPolygon->setNormal(nIndex, rValue);
}
void B3DPolygon::transformNormals(const B3DHomMatrix& rMatrix)
{
if(mpPolygon->areNormalsUsed() && !rMatrix.isIdentity())
mpPolygon->transformNormals(rMatrix);
}
bool B3DPolygon::areNormalsUsed() const
{
return mpPolygon->areNormalsUsed();
}
void B3DPolygon::clearNormals()
{
if(mpPolygon->areNormalsUsed())
mpPolygon->clearNormals();
}
B2DPoint B3DPolygon::getTextureCoordinate(sal_uInt32 nIndex) const
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
return mpPolygon->getTextureCoordinate(nIndex);
}
void B3DPolygon::setTextureCoordinate(sal_uInt32 nIndex, const B2DPoint& rValue)
{
OSL_ENSURE(nIndex < mpPolygon->count(), "B3DPolygon access outside range (!)");
if(mpPolygon->getTextureCoordinate(nIndex) != rValue)
mpPolygon->setTextureCoordinate(nIndex, rValue);
}
void B3DPolygon::transformTextureCoordiantes(const B2DHomMatrix& rMatrix)
{
if(mpPolygon->areTextureCoordinatesUsed() && !rMatrix.isIdentity())
mpPolygon->transformTextureCoordinates(rMatrix);
}
bool B3DPolygon::areTextureCoordinatesUsed() const
{
return mpPolygon->areTextureCoordinatesUsed();
}
void B3DPolygon::clearTextureCoordinates()
{
if(mpPolygon->areTextureCoordinatesUsed())
mpPolygon->clearTextureCoordinates();
}
void B3DPolygon::insert(sal_uInt32 nIndex, const ::basegfx::B3DPoint& rPoint, sal_uInt32 nCount)
{
OSL_ENSURE(nIndex <= mpPolygon->count(), "B3DPolygon Insert outside range (!)");
if(nCount)
mpPolygon->insert(nIndex, rPoint, nCount);
}
void B3DPolygon::append(const basegfx::B3DPoint& rPoint, sal_uInt32 nCount)
{
if(nCount)
mpPolygon->insert(mpPolygon->count(), rPoint, nCount);
}
void B3DPolygon::insert(sal_uInt32 nIndex, const B3DPolygon& rPoly, sal_uInt32 nIndex2, sal_uInt32 nCount)
{
OSL_ENSURE(nIndex <= mpPolygon->count(), "B3DPolygon Insert outside range (!)");
if(rPoly.count())
{
if(!nCount)
{
nCount = rPoly.count();
}
if(0L == nIndex2 && nCount == rPoly.count())
{
mpPolygon->insert(nIndex, *rPoly.mpPolygon);
}
else
{
OSL_ENSURE(nIndex2 + nCount <= rPoly.mpPolygon->count(), "B3DPolygon Insert outside range (!)");
ImplB3DPolygon aTempPoly(*rPoly.mpPolygon, nIndex2, nCount);
mpPolygon->insert(nIndex, aTempPoly);
}
}
}
void B3DPolygon::append(const B3DPolygon& rPoly, sal_uInt32 nIndex, sal_uInt32 nCount)
{
if(rPoly.count())
{
if(!nCount)
{
nCount = rPoly.count();
}
if(0L == nIndex && nCount == rPoly.count())
{
mpPolygon->insert(mpPolygon->count(), *rPoly.mpPolygon);
}
else
{
OSL_ENSURE(nIndex + nCount <= rPoly.mpPolygon->count(), "B3DPolygon Append outside range (!)");
ImplB3DPolygon aTempPoly(*rPoly.mpPolygon, nIndex, nCount);
mpPolygon->insert(mpPolygon->count(), aTempPoly);
}
}
}
void B3DPolygon::remove(sal_uInt32 nIndex, sal_uInt32 nCount)
{
OSL_ENSURE(nIndex + nCount <= mpPolygon->count(), "B3DPolygon Remove outside range (!)");
if(nCount)
mpPolygon->remove(nIndex, nCount);
}
void B3DPolygon::clear()
{
mpPolygon = DefaultPolygon::get();
}
bool B3DPolygon::isClosed() const
{
return mpPolygon->isClosed();
}
void B3DPolygon::setClosed(bool bNew)
{
if(isClosed() != bNew)
mpPolygon->setClosed(bNew);
}
void B3DPolygon::flip()
{
if(count() > 1)
mpPolygon->flip();
}
bool B3DPolygon::hasDoublePoints() const
{
return (mpPolygon->count() > 1L && mpPolygon->hasDoublePoints());
}
void B3DPolygon::removeDoublePoints()
{
if(hasDoublePoints())
{
mpPolygon->removeDoublePointsAtBeginEnd();
mpPolygon->removeDoublePointsWholeTrack();
}
}
void B3DPolygon::transform(const basegfx::B3DHomMatrix& rMatrix)
{
if(mpPolygon->count() && !rMatrix.isIdentity())
{
mpPolygon->transform(rMatrix);
}
}
} // end of namespace basegfx
//////////////////////////////////////////////////////////////////////////////
// eof