/**************************************************************
 * 
 * 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_tools.hxx"
#include <tools/b3dtrans.hxx>
#include <tools/debug.hxx>

/*************************************************************************
|*
|* Transformationen fuer alle 3D Ausgaben
|*
\************************************************************************/

B3dTransformationSet::B3dTransformationSet()
{
	Reset();
}

B3dTransformationSet::~B3dTransformationSet()
{
}

void B3dTransformationSet::Orientation(basegfx::B3DHomMatrix& rTarget, basegfx::B3DPoint aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP)
{
	rTarget.translate( -aVRP.getX(), -aVRP.getY(), -aVRP.getZ());
	aVUP.normalize();
	aVPN.normalize();
	basegfx::B3DVector aRx(aVUP);
	basegfx::B3DVector aRy(aVPN);
	aRx = aRx.getPerpendicular(aRy);
	aRx.normalize();
	aRy = aRy.getPerpendicular(aRx);
	aRy.normalize();
	basegfx::B3DHomMatrix aTemp;
	aTemp.set(0, 0, aRx.getX());
	aTemp.set(0, 1, aRx.getY());
	aTemp.set(0, 2, aRx.getZ());
	aTemp.set(1, 0, aRy.getX());
	aTemp.set(1, 1, aRy.getY());
	aTemp.set(1, 2, aRy.getZ());
	aTemp.set(2, 0, aVPN.getX());
	aTemp.set(2, 1, aVPN.getY());
	aTemp.set(2, 2, aVPN.getZ());
	rTarget *= aTemp;
}

void B3dTransformationSet::Frustum(basegfx::B3DHomMatrix& rTarget, double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
{
	if(!(fNear > 0.0))
	{
		fNear = 0.001;
	}
	if(!(fFar > 0.0))
	{
		fFar = 1.0;
	}
	if(fNear == fFar)
	{
		fFar = fNear + 1.0;
	}
	if(fLeft == fRight)
	{
		fLeft -= 1.0;
		fRight += 1.0;
	}
	if(fTop == fBottom)
	{
		fBottom -= 1.0;
		fTop += 1.0;
	}
	basegfx::B3DHomMatrix aTemp;

	aTemp.set(0, 0, 2.0 * fNear / (fRight - fLeft));
	aTemp.set(1, 1, 2.0 * fNear / (fTop - fBottom));
	aTemp.set(0, 2, (fRight + fLeft) / (fRight - fLeft));
	aTemp.set(1, 2, (fTop + fBottom) / (fTop - fBottom));
	aTemp.set(2, 2, -1.0 * ((fFar + fNear) / (fFar - fNear)));
	aTemp.set(3, 2, -1.0);
	aTemp.set(2, 3, -1.0 * ((2.0 * fFar * fNear) / (fFar - fNear)));
	aTemp.set(3, 3, 0.0);

	rTarget *= aTemp;
}

void B3dTransformationSet::Ortho(basegfx::B3DHomMatrix& rTarget, double fLeft, double fRight, double fBottom, double fTop, double fNear, double fFar)
{
	if(fNear == fFar)
	{
		DBG_ERROR("Near and far clipping plane in Ortho definition are identical");
		fFar = fNear + 1.0;
	}
	if(fLeft == fRight)
	{
		DBG_ERROR("Left and right in Ortho definition are identical");
		fLeft -= 1.0;
		fRight += 1.0;
	}
	if(fTop == fBottom)
	{
		DBG_ERROR("Top and bottom in Ortho definition are identical");
		fBottom -= 1.0;
		fTop += 1.0;
	}
	basegfx::B3DHomMatrix aTemp;

	aTemp.set(0, 0, 2.0 / (fRight - fLeft));
	aTemp.set(1, 1, 2.0 / (fTop - fBottom));
	aTemp.set(2, 2, -1.0 * (2.0 / (fFar - fNear)));
	aTemp.set(0, 3, -1.0 * ((fRight + fLeft) / (fRight - fLeft)));
	aTemp.set(1, 3, -1.0 * ((fTop + fBottom) / (fTop - fBottom)));
	aTemp.set(2, 3, -1.0 * ((fFar + fNear) / (fFar - fNear)));

	rTarget *= aTemp;
}

/*************************************************************************
|*
|* Reset der Werte
|*
\************************************************************************/

void B3dTransformationSet::Reset()
{
	// Matritzen auf Einheitsmatritzen
	maObjectTrans.identity();
	PostSetObjectTrans();

	Orientation(maOrientation);
	PostSetOrientation();

	maTexture.identity();

	mfLeftBound = mfBottomBound = -1.0;
	mfRightBound = mfTopBound = 1.0;
	mfNearBound = 0.001;
	mfFarBound = 1.001;

	meRatio = Base3DRatioGrow;
	mfRatio = 0.0;

	maViewportRectangle = Rectangle(-1, -1, 2, 2);
	maVisibleRectangle = maViewportRectangle;

	mbPerspective = sal_True;

	mbProjectionValid = sal_False;
	mbObjectToDeviceValid = sal_False;
	mbWorldToViewValid = sal_False;

	CalcViewport();
}

/*************************************************************************
|*
|* Objekttransformation
|*
\************************************************************************/

void B3dTransformationSet::SetObjectTrans(const basegfx::B3DHomMatrix& rObj)
{
	maObjectTrans = rObj;

	mbObjectToDeviceValid = sal_False;
	mbInvTransObjectToEyeValid = sal_False;

	PostSetObjectTrans();
}

void B3dTransformationSet::PostSetObjectTrans()
{
	// Zuweisen und Inverse bestimmen
	maInvObjectTrans = maObjectTrans;
	maInvObjectTrans.invert();
}

/*************************************************************************
|*
|* Orientierungstransformation
|*
\************************************************************************/

void B3dTransformationSet::SetOrientation( basegfx::B3DPoint aVRP, basegfx::B3DVector aVPN, basegfx::B3DVector aVUP)
{
	maOrientation.identity();
	Orientation(maOrientation, aVRP, aVPN, aVUP);

	mbInvTransObjectToEyeValid = sal_False;
	mbObjectToDeviceValid = sal_False;
	mbWorldToViewValid = sal_False;

	PostSetOrientation();
}

void B3dTransformationSet::SetOrientation(basegfx::B3DHomMatrix& mOrient)
{
	maOrientation = mOrient;

	mbInvTransObjectToEyeValid = sal_False;
	mbObjectToDeviceValid = sal_False;
	mbWorldToViewValid = sal_False;

	PostSetOrientation();
}

void B3dTransformationSet::PostSetOrientation()
{
	// Zuweisen und Inverse bestimmen
	maInvOrientation = maOrientation;
	maInvOrientation.invert();
}

/*************************************************************************
|*
|* Projektionstransformation
|*
\************************************************************************/

void B3dTransformationSet::SetProjection(const basegfx::B3DHomMatrix& mProject)
{
	maProjection = mProject;
	PostSetProjection();
}

const basegfx::B3DHomMatrix& B3dTransformationSet::GetProjection()
{
	if(!mbProjectionValid)
		CalcViewport();
	return maProjection;
}

const basegfx::B3DHomMatrix& B3dTransformationSet::GetInvProjection()
{
	if(!mbProjectionValid)
		CalcViewport();
	return maInvProjection;
}

void B3dTransformationSet::PostSetProjection()
{
	// Zuweisen und Inverse bestimmen
	maInvProjection = GetProjection();
	maInvProjection.invert();

	// Abhaengige Matritzen invalidieren
	mbObjectToDeviceValid = sal_False;
	mbWorldToViewValid = sal_False;
}

/*************************************************************************
|*
|* Texturtransformation
|*
\************************************************************************/

void B3dTransformationSet::SetTexture(const basegfx::B2DHomMatrix& rTxt)
{
	maTexture = rTxt;
	PostSetTexture();
}

void B3dTransformationSet::PostSetTexture()
{
}

/*************************************************************************
|*
|* Viewport-Transformation
|*
\************************************************************************/

void B3dTransformationSet::CalcViewport()
{
	// Faktoren fuer die Projektion
	double fLeft(mfLeftBound);
	double fRight(mfRightBound);
	double fBottom(mfBottomBound);
	double fTop(mfTopBound);

	// Soll das Seitenverhaeltnis Beachtung finden?
	// Falls ja, Bereich der Projektion an Seitenverhaeltnis anpassen
	if(GetRatio() != 0.0)
	{
		// Berechne aktuelles Seitenverhaeltnis der Bounds
		double fBoundWidth = (double)(maViewportRectangle.GetWidth() + 1);
		double fBoundHeight = (double)(maViewportRectangle.GetHeight() + 1);
		double fActRatio = 1;
		double fFactor;

		if(fBoundWidth != 0.0)
			fActRatio = fBoundHeight / fBoundWidth;
		// FIXME   else in this case has a lot of problems,  should this return.

		switch(meRatio)
		{
			case Base3DRatioShrink :
			{
				// Kleineren Teil vergroessern
				if(fActRatio > mfRatio)
				{
					// X vergroessern
					fFactor = 1.0 / fActRatio;
					fRight	*= fFactor;
					fLeft *= fFactor;
				}
				else
				{
					// Y vergroessern
					fFactor = fActRatio;
					fTop *= fFactor;
					fBottom *= fFactor;
				}
				break;
			}
			case Base3DRatioGrow :
			{
				// GroesserenTeil verkleinern
				if(fActRatio > mfRatio)
				{
					// Y verkleinern
					fFactor = fActRatio;
					fTop *= fFactor;
					fBottom *= fFactor;
				}
				else
				{
					// X verkleinern
					fFactor = 1.0 / fActRatio;
					fRight	*= fFactor;
					fLeft *= fFactor;
				}
				break;
			}
			case Base3DRatioMiddle :
			{
				// Mitteln
				fFactor = ((1.0 / fActRatio) + 1.0) / 2.0;
				fRight *= fFactor;
				fLeft *= fFactor;
				fFactor = (fActRatio + 1.0) / 2.0;
				fTop *= fFactor;
				fBottom *= fFactor;
				break;
			}
		}
	}

	// Ueberschneiden sich Darstellungsflaeche und Objektflaeche?
	maSetBound = maViewportRectangle;

	// Mit den neuen Werten Projektion und ViewPort setzen
	basegfx::B3DHomMatrix aNewProjection;

	// #i36281#
	// OpenGL needs a little more rough additional size to not let
	// the front face vanish. Changed from SMALL_DVALUE to 0.000001,
	// which is 1/10000th, comared with 1/tenth of a million from SMALL_DVALUE.
	const double fDistPart((mfFarBound - mfNearBound) * 0.0001);

	// Near, Far etwas grosszuegiger setzen, um falsches,
	// zu kritisches clippen zu verhindern
	if(mbPerspective)
	{
		Frustum(aNewProjection, fLeft, fRight, fBottom, fTop, mfNearBound - fDistPart, mfFarBound + fDistPart);
	}
	else
	{
		Ortho(aNewProjection, fLeft, fRight, fBottom, fTop, mfNearBound - fDistPart, mfFarBound + fDistPart);
	}

	// jetzt schon auf gueltig setzen um Endlosschleife zu vermeiden
	mbProjectionValid = sal_True;

	// Neue Projektion setzen
	SetProjection(aNewProjection);

	// fill parameters for ViewportTransformation
	// Translation
	maTranslate.setX((double)maSetBound.Left() + ((maSetBound.GetWidth() - 1L) / 2.0));
	maTranslate.setY((double)maSetBound.Top() + ((maSetBound.GetHeight() - 1L) / 2.0));
	maTranslate.setZ(ZBUFFER_DEPTH_RANGE / 2.0);

	// Skalierung
	maScale.setX((maSetBound.GetWidth() - 1L) / 2.0);
	maScale.setY((maSetBound.GetHeight() - 1L) / -2.0);
	maScale.setZ(ZBUFFER_DEPTH_RANGE / 2.0);

	// Auf Veraenderung des ViewPorts reagieren
	PostSetViewport();
}

void B3dTransformationSet::SetRatio(double fNew)
{
	if(mfRatio != fNew)
	{
		mfRatio = fNew;
		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;
	}
}

void B3dTransformationSet::SetRatioMode(Base3DRatio eNew)
{
	if(meRatio != eNew)
	{
		meRatio = eNew;
		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;
	}
}

void B3dTransformationSet::SetDeviceRectangle(double fL, double fR, double fB, double fT,
	sal_Bool bBroadCastChange)
{
	if(fL != mfLeftBound || fR != mfRightBound || fB != mfBottomBound || fT != mfTopBound)
	{
		mfLeftBound = fL;
		mfRightBound = fR;
		mfBottomBound = fB;
		mfTopBound = fT;

		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;

		// Aenderung bekanntmachen
		if(bBroadCastChange)
			DeviceRectangleChange();
	}
}

void B3dTransformationSet::SetDeviceVolume(const basegfx::B3DRange& rVol, sal_Bool bBroadCastChange)
{
	SetDeviceRectangle(rVol.getMinX(), rVol.getMaxX(), rVol.getMinY(), rVol.getMaxY(), bBroadCastChange);
	SetFrontClippingPlane(rVol.getMinZ());
	SetBackClippingPlane(rVol.getMaxZ());
}

void B3dTransformationSet::DeviceRectangleChange()
{
}

void B3dTransformationSet::GetDeviceRectangle(double &fL, double &fR, double& fB, double& fT)
{
	fL = mfLeftBound;
	fR = mfRightBound;
	fB = mfBottomBound;
	fT = mfTopBound;

	mbProjectionValid = sal_False;
	mbObjectToDeviceValid = sal_False;
	mbWorldToViewValid = sal_False;
}

basegfx::B3DRange B3dTransformationSet::GetDeviceVolume()
{
	basegfx::B3DRange aRet;

	aRet.expand(basegfx::B3DTuple(mfLeftBound, mfBottomBound, mfNearBound));
	aRet.expand(basegfx::B3DTuple(mfRightBound, mfTopBound, mfFarBound));

	return aRet;
}

void B3dTransformationSet::SetFrontClippingPlane(double fF)
{
	if(mfNearBound != fF)
	{
		mfNearBound = fF;
		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;
	}
}

void B3dTransformationSet::SetBackClippingPlane(double fB)
{
	if(mfFarBound != fB)
	{
		mfFarBound = fB;
		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;
	}
}

void B3dTransformationSet::SetPerspective(sal_Bool bNew)
{
	if(mbPerspective != bNew)
	{
		mbPerspective = bNew;
		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;
	}
}

void B3dTransformationSet::SetViewportRectangle(Rectangle& rRect, Rectangle& rVisible)
{
	if(rRect != maViewportRectangle || rVisible != maVisibleRectangle)
	{
		maViewportRectangle = rRect;
		maVisibleRectangle = rVisible;

		mbProjectionValid = sal_False;
		mbObjectToDeviceValid = sal_False;
		mbWorldToViewValid = sal_False;
	}
}

void B3dTransformationSet::PostSetViewport()
{
}

const Rectangle& B3dTransformationSet::GetLogicalViewportBounds()
{
	if(!mbProjectionValid)
		CalcViewport();
	return maSetBound;
}

const basegfx::B3DVector& B3dTransformationSet::GetScale()
{
	if(!mbProjectionValid)
		CalcViewport();
	return maScale;
}

const basegfx::B3DVector& B3dTransformationSet::GetTranslate()
{
	if(!mbProjectionValid)
		CalcViewport();
	return maTranslate;
}

/*************************************************************************
|*
|* Hilfsmatrixberechnungsroutinen
|*
\************************************************************************/

void B3dTransformationSet::CalcMatObjectToDevice()
{
	// ObjectToDevice berechnen (Orientation * Projection * Object)
	maObjectToDevice = maObjectTrans;
	maObjectToDevice *= maOrientation;
	maObjectToDevice *= GetProjection();

	// auf gueltig setzen
	mbObjectToDeviceValid = sal_True;
}

const basegfx::B3DHomMatrix& B3dTransformationSet::GetObjectToDevice()
{
	if(!mbObjectToDeviceValid)
		CalcMatObjectToDevice();
	return maObjectToDevice;
}

void B3dTransformationSet::CalcMatInvTransObjectToEye()
{
	maInvTransObjectToEye = maObjectTrans;
	maInvTransObjectToEye *= maOrientation;
	maInvTransObjectToEye.invert();
	maInvTransObjectToEye.transpose();

	// eventuelle Translationen rausschmeissen, da diese
	// Matrix nur zur Transformation von Vektoren gedacht ist
	maInvTransObjectToEye.set(3, 0, 0.0);
	maInvTransObjectToEye.set(3, 1, 0.0);
	maInvTransObjectToEye.set(3, 2, 0.0);
	maInvTransObjectToEye.set(3, 3, 1.0);

	// auf gueltig setzen
	mbInvTransObjectToEyeValid = sal_True;
}

const basegfx::B3DHomMatrix& B3dTransformationSet::GetInvTransObjectToEye()
{
	if(!mbInvTransObjectToEyeValid)
		CalcMatInvTransObjectToEye();
	return maInvTransObjectToEye;
}

basegfx::B3DHomMatrix B3dTransformationSet::GetMatFromObjectToView()
{
	basegfx::B3DHomMatrix aFromObjectToView = GetObjectToDevice();

	const basegfx::B3DVector& rScale(GetScale());
	aFromObjectToView.scale(rScale.getX(), rScale.getY(), rScale.getZ());
	const basegfx::B3DVector& rTranslate(GetTranslate());
	aFromObjectToView.translate(rTranslate.getX(), rTranslate.getY(), rTranslate.getZ());

	return aFromObjectToView;
}

void B3dTransformationSet::CalcMatFromWorldToView()
{
	maMatFromWorldToView = maOrientation;
	maMatFromWorldToView *= GetProjection();
	const basegfx::B3DVector& rScale(GetScale());
	maMatFromWorldToView.scale(rScale.getX(), rScale.getY(), rScale.getZ());
	const basegfx::B3DVector& rTranslate(GetTranslate());
	maMatFromWorldToView.translate(rTranslate.getX(), rTranslate.getY(), rTranslate.getZ());
	maInvMatFromWorldToView = maMatFromWorldToView;
	maInvMatFromWorldToView.invert();

	// gueltig setzen
	mbWorldToViewValid = sal_True;
}

const basegfx::B3DHomMatrix& B3dTransformationSet::GetMatFromWorldToView()
{
	if(!mbWorldToViewValid)
		CalcMatFromWorldToView();
	return maMatFromWorldToView;
}

const basegfx::B3DHomMatrix& B3dTransformationSet::GetInvMatFromWorldToView()
{
	if(!mbWorldToViewValid)
		CalcMatFromWorldToView();
	return maInvMatFromWorldToView;
}

/*************************************************************************
|*
|* Direkter Zugriff auf verschiedene Transformationen
|*
\************************************************************************/

const basegfx::B3DPoint B3dTransformationSet::WorldToEyeCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetOrientation();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::EyeToWorldCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvOrientation();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::EyeToViewCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetProjection();
	aVec *= GetScale();
	aVec += GetTranslate();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ViewToEyeCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec -= GetTranslate();
	aVec = aVec / GetScale();
	aVec *= GetInvProjection();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::WorldToViewCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetMatFromWorldToView();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ViewToWorldCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvMatFromWorldToView();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::DeviceToViewCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetScale();
	aVec += GetTranslate();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ViewToDeviceCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec -= GetTranslate();
	aVec = aVec / GetScale();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ObjectToWorldCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetObjectTrans();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::WorldToObjectCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvObjectTrans();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ObjectToViewCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetObjectTrans();
	aVec *= GetMatFromWorldToView();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ViewToObjectCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvMatFromWorldToView();
	aVec *= GetInvObjectTrans();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::ObjectToEyeCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetObjectTrans();
	aVec *= GetOrientation();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::EyeToObjectCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvOrientation();
	aVec *= GetInvObjectTrans();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::DeviceToEyeCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvProjection();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::EyeToDeviceCoor(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetProjection();
	return aVec;
}

const basegfx::B3DPoint B3dTransformationSet::InvTransObjectToEye(const basegfx::B3DPoint& rVec)
{
	basegfx::B3DPoint aVec(rVec);
	aVec *= GetInvTransObjectToEye();
	return aVec;
}

const basegfx::B2DPoint B3dTransformationSet::TransTextureCoor(const basegfx::B2DPoint& rVec)
{
	basegfx::B2DPoint aVec(rVec);
	aVec *= GetTexture();
	return aVec;
}

/*************************************************************************
|*
|* Konstruktor B3dViewport
|*
\************************************************************************/

B3dViewport::B3dViewport()
:	B3dTransformationSet(),
	aVRP(0, 0, 0),
	aVPN(0, 0, 1),
	aVUV(0, 1, 0)
{
	CalcOrientation();
}

B3dViewport::~B3dViewport()
{
}

void B3dViewport::SetVRP(const basegfx::B3DPoint& rNewVRP)
{
	aVRP = rNewVRP;
	CalcOrientation();
}

void B3dViewport::SetVPN(const basegfx::B3DVector& rNewVPN)
{
	aVPN = rNewVPN;
	CalcOrientation();
}

void B3dViewport::SetVUV(const basegfx::B3DVector& rNewVUV)
{
	aVUV = rNewVUV;
	CalcOrientation();
}

void B3dViewport::SetViewportValues(
	const basegfx::B3DPoint& rNewVRP,
	const basegfx::B3DVector& rNewVPN,
	const basegfx::B3DVector& rNewVUV)
{
	aVRP = rNewVRP;
	aVPN = rNewVPN;
	aVUV = rNewVUV;
	CalcOrientation();
}

void B3dViewport::CalcOrientation()
{
	SetOrientation(aVRP, aVPN, aVUV);
}

/*************************************************************************
|*
|* Konstruktor B3dViewport
|*
\************************************************************************/

B3dCamera::B3dCamera(
	const basegfx::B3DPoint& rPos, const basegfx::B3DVector& rLkAt,
	double fFocLen, double fBnkAng, sal_Bool bUseFocLen)
:	B3dViewport(),
	aPosition(rPos),
	aCorrectedPosition(rPos),
	aLookAt(rLkAt),
	fFocalLength(fFocLen),
	fBankAngle(fBnkAng),
	bUseFocalLength(bUseFocLen)
{
	CalcNewViewportValues();
}

B3dCamera::~B3dCamera()
{
}

void B3dCamera::SetPosition(const basegfx::B3DPoint& rNewPos)
{
	if(rNewPos != aPosition)
	{
		// Zuweisen
		aCorrectedPosition = aPosition = rNewPos;

		// Neuberechnung
		CalcNewViewportValues();
	}
}

void B3dCamera::SetLookAt(const basegfx::B3DVector& rNewLookAt)
{
	if(rNewLookAt != aLookAt)
	{
		// Zuweisen
		aLookAt = rNewLookAt;

		// Neuberechnung
		CalcNewViewportValues();
	}
}

void B3dCamera::SetPositionAndLookAt(const basegfx::B3DPoint& rNewPos, const basegfx::B3DVector& rNewLookAt)
{
	if(rNewPos != aPosition || rNewLookAt != aLookAt)
	{
		// Zuweisen
		aPosition = rNewPos;
		aLookAt = rNewLookAt;

		// Neuberechnung
		CalcNewViewportValues();
	}
}

void B3dCamera::SetFocalLength(double fLen)
{
	if(fLen != fFocalLength)
	{
		// Zuweisen
		if(fLen < 5.0)
			fLen = 5.0;
		fFocalLength = fLen;

		// Neuberechnung
		CalcNewViewportValues();
	}
}

void B3dCamera::SetBankAngle(double fAngle)
{
	if(fAngle != fBankAngle)
	{
		// Zuweisen
		fBankAngle = fAngle;

		// Neuberechnung
		CalcNewViewportValues();
	}
}

void B3dCamera::SetUseFocalLength(sal_Bool bNew)
{
	if(bNew != (sal_Bool)bUseFocalLength)
	{
		// Zuweisen
		bUseFocalLength = bNew;

		// Neuberechnung
		CalcNewViewportValues();
	}
}

void B3dCamera::DeviceRectangleChange()
{
	// call parent
	B3dViewport::DeviceRectangleChange();

	// Auf Aenderung reagieren
	CalcNewViewportValues();
}

void B3dCamera::CalcNewViewportValues()
{
	basegfx::B3DVector aViewVector(aPosition - aLookAt);
	basegfx::B3DVector aNewVPN(aViewVector);

	basegfx::B3DVector aNewVUV(0.0, 1.0, 0.0);
	if(aNewVPN.getLength() < aNewVPN.getY())
		aNewVUV.setX(0.5);

	aNewVUV.normalize();
	aNewVPN.normalize();

	basegfx::B3DVector aNewToTheRight = aNewVPN;
	aNewToTheRight = aNewToTheRight.getPerpendicular(aNewVUV);
	aNewToTheRight.normalize();
	aNewVUV = aNewToTheRight.getPerpendicular(aNewVPN);
	aNewVUV.normalize();

	SetViewportValues(aPosition, aNewVPN, aNewVUV);
	if(CalcFocalLength())
		SetViewportValues(aCorrectedPosition, aNewVPN, aNewVUV);

	if(fBankAngle != 0.0)
	{
		basegfx::B3DHomMatrix aRotMat;
		aRotMat.rotate(0.0, 0.0, fBankAngle);
		basegfx::B3DVector aUp(0.0, 1.0, 0.0);
		aUp *= aRotMat;
		aUp = EyeToWorldCoor(aUp);
		aUp.normalize();
		SetVUV(aUp);
	}
}

sal_Bool B3dCamera::CalcFocalLength()
{
	double fWidth = GetDeviceRectangleWidth();
	sal_Bool bRetval = sal_False;

	if(bUseFocalLength)
	{
		// Position aufgrund der FocalLength korrigieren
		aCorrectedPosition = basegfx::B3DPoint(0.0, 0.0, fFocalLength * fWidth / 35.0);
		aCorrectedPosition = EyeToWorldCoor(aCorrectedPosition);
		bRetval = sal_True;
	}
	else
	{
		// FocalLength anhand der Position anpassen
		basegfx::B3DPoint aOldPosition;
		aOldPosition = WorldToEyeCoor(aOldPosition);
		if(fWidth != 0.0)
			fFocalLength = aOldPosition.getZ() / fWidth * 35.0;
		if(fFocalLength < 5.0)
			fFocalLength = 5.0;
	}
	return bRetval;
}

// eof
