/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* 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 .
*/
#include <tools/b3dtrans.hxx>
#include <osl/diagnose.h>
// Near and far clipping planes
constexpr double gfNearBound = 0 .001 ;
constexpr double gfFarBound = 1 .001 ;
// B3dTransformationSet --------------------------------------------------------
// Transformations for all 3D output
B3dTransformationSet::B3dTransformationSet()
{
Reset();
}
B3dTransformationSet::~B3dTransformationSet()
{
}
void B3dTransformationSet::Orientation(basegfx::B3DHomMatrix& rTarget, const 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)
{
OSL_FAIL("Near and far clipping plane in Ortho definition are identical" );
fFar = fNear + 1 .0 ;
}
if (fLeft == fRight)
{
OSL_FAIL("Left and right in Ortho definition are identical" );
fLeft -= 1 .0 ;
fRight += 1 .0 ;
}
if (fTop == fBottom)
{
OSL_FAIL("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 values
void B3dTransformationSet::Reset()
{
// Reset matrices to identity matrices
maObjectTrans.identity();
PostSetObjectTrans();
Orientation(maOrientation);
PostSetOrientation();
maTexture.identity();
mfLeftBound = mfBottomBound = -1 .0 ;
mfRightBound = mfTopBound = 1 .0 ;
mfRatio = 0 .0 ;
maViewportRectangle = tools::Rectangle(-1 , -1 , 2 , 2 );
maVisibleRectangle = maViewportRectangle;
mbPerspective = true ;
mbProjectionValid = false ;
CalcViewport();
}
/// Object transformation
void B3dTransformationSet::PostSetObjectTrans()
{
// Assign and compute inverse
maInvObjectTrans = maObjectTrans;
maInvObjectTrans.invert();
}
void B3dTransformationSet::SetOrientation(const basegfx::B3DPoint& rVRP, const basegfx::B3DVector& rVPN, const basegfx::B3DVector& rVUP)
{
maOrientation.identity();
Orientation(maOrientation, rVRP, rVPN, rVUP);
PostSetOrientation();
}
void B3dTransformationSet::PostSetOrientation()
{
// Assign and compute inverse
maInvOrientation = maOrientation;
maInvOrientation.invert();
}
/// Projections for transformations
void B3dTransformationSet::SetProjection(const basegfx::B3DHomMatrix& mProject)
{
maProjection = mProject;
PostSetProjection();
}
const basegfx::B3DHomMatrix& B3dTransformationSet::GetProjection()
{
if (!mbProjectionValid)
CalcViewport();
return maProjection;
}
void B3dTransformationSet::PostSetProjection()
{
// Assign and compute inverse
maInvProjection = GetProjection();
maInvProjection.invert();
}
/// Transformations for viewport
void B3dTransformationSet::CalcViewport()
{
// Parameters for projection
double fLeft(mfLeftBound);
double fRight(mfRightBound);
double fBottom(mfBottomBound);
double fTop(mfTopBound);
// Adjust projection to aspect ratio, if set
if (GetRatio() != 0 .0 )
{
// Compute current aspect ratio of boundaries
double fBoundWidth = static_cast <double >(maViewportRectangle.GetWidth() + 1 );
double fBoundHeight = static_cast <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.
// scale down larger part
if (fActRatio > mfRatio)
{
// scale down Y
fFactor = fActRatio;
fTop *= fFactor;
fBottom *= fFactor;
}
else
{
// scale down X
fFactor = 1 .0 / fActRatio;
fRight *= fFactor;
fLeft *= fFactor;
}
}
// Do projection and object areas overlap?
maSetBound = maViewportRectangle;
// Reset projection with new values
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, compared with 1/tenth of a million from SMALL_DVALUE.
const double fDistPart((gfFarBound - gfNearBound) * 0 .0001 );
// To avoid critical clipping, set Near & Far generously
if (mbPerspective)
{
Frustum(aNewProjection, fLeft, fRight, fBottom, fTop, gfNearBound - fDistPart, gfFarBound + fDistPart);
}
else
{
Ortho(aNewProjection, fLeft, fRight, fBottom, fTop, gfNearBound - fDistPart, gfFarBound + fDistPart);
}
// Set to true to guarantee loop termination
mbProjectionValid = true ;
// set new projection
SetProjection(aNewProjection);
// fill parameters for ViewportTransformation
// Translation
maTranslate.setX(static_cast <double >(maSetBound.Left()) + ((maSetBound.GetWidth() - 1 ) / 2 .0 ));
maTranslate.setY(static_cast <double >(maSetBound.Top()) + ((maSetBound.GetHeight() - 1 ) / 2 .0 ));
maTranslate.setZ(ZBUFFER_DEPTH_RANGE / 2 .0 );
// Scaling
maScale.setX((maSetBound.GetWidth() - 1 ) / 2 .0 );
maScale.setY((maSetBound.GetHeight() - 1 ) / -2 .0 );
maScale.setZ(ZBUFFER_DEPTH_RANGE / 2 .0 );
}
void B3dTransformationSet::SetRatio(double fNew)
{
if (mfRatio != fNew)
{
mfRatio = fNew;
mbProjectionValid = false ;
}
}
void B3dTransformationSet::SetDeviceRectangle(double fL, double fR, double fB, double fT)
{
if (fL != mfLeftBound || fR != mfRightBound || fB != mfBottomBound || fT != mfTopBound)
{
mfLeftBound = fL;
mfRightBound = fR;
mfBottomBound = fB;
mfTopBound = fT;
mbProjectionValid = false ;
// Broadcast changes
DeviceRectangleChange();
}
}
void B3dTransformationSet::DeviceRectangleChange()
{
}
void B3dTransformationSet::SetPerspective(bool bNew)
{
if (mbPerspective != bNew)
{
mbPerspective = bNew;
mbProjectionValid = false ;
}
}
void B3dTransformationSet::SetViewportRectangle(tools::Rectangle const & rRect, tools::Rectangle const & rVisible)
{
if (rRect != maViewportRectangle || rVisible != maVisibleRectangle)
{
maViewportRectangle = rRect;
maVisibleRectangle = rVisible;
mbProjectionValid = false ;
}
}
// direct access to various transformations
basegfx::B3DPoint B3dTransformationSet::WorldToEyeCoor(const basegfx::B3DPoint& rVec)
{
basegfx::B3DPoint aVec(rVec);
aVec *= maOrientation;
return aVec;
}
basegfx::B3DPoint B3dTransformationSet::EyeToWorldCoor(const basegfx::B3DPoint& rVec)
{
basegfx::B3DPoint aVec(rVec);
aVec *= maInvOrientation;
return aVec;
}
// B3dViewport -----------------------------------------------------------------
B3dViewport::B3dViewport()
: aVRP(0 , 0 , 0 ),
aVPN(0 , 0 , 1 ),
aVUV(0 , 1 , 0 )
{
CalcOrientation();
}
B3dViewport::~B3dViewport()
{
}
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);
}
// B3dCamera -------------------------------------------------------------------
B3dCamera::B3dCamera(
const basegfx::B3DPoint& rPos, const basegfx::B3DVector& rLkAt,
double fFocLen, double fBnkAng)
: aPosition(rPos),
aLookAt(rLkAt),
fFocalLength(fFocLen),
fBankAngle(fBnkAng)
{
CalcNewViewportValues();
}
B3dCamera::~B3dCamera()
{
}
void B3dCamera::DeviceRectangleChange()
{
// call parent
B3dViewport::DeviceRectangleChange();
// react to changes
CalcNewViewportValues();
}
void B3dCamera::CalcNewViewportValues()
{
basegfx::B3DVector aNewVPN(aPosition - aLookAt);
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.getPerpendicular(aNewVUV);
aNewToTheRight.normalize();
aNewVUV = aNewToTheRight.getPerpendicular(aNewVPN);
aNewVUV.normalize();
SetViewportValues(aPosition, aNewVPN, aNewVUV);
CalcFocalLength();
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);
}
}
void B3dCamera::CalcFocalLength()
{
double fWidth = GetDeviceRectangleWidth();
// Adjust focal length based on given position
basegfx::B3DPoint aOldPosition = WorldToEyeCoor({});
if (fWidth != 0 .0 )
fFocalLength = aOldPosition.getZ() / fWidth * 35 .0 ;
if (fFocalLength < 5 .0 )
fFocalLength = 5 .0 ;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=85 H=96 G=90
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland