<commit_before>#include <Gui/Viewer/TrackballCameraManipulator.hpp>

#include <Core/Asset/Camera.hpp>
#include <Core/Math/Math.hpp>
#include <Core/Utils/Log.hpp>
#include <Engine/Scene/Light.hpp>
#include <Gui/Utils/Keyboard.hpp>

#include <Gui/Utils/KeyMappingManager.hpp>

#include <Engine/Scene/CameraComponent.hpp>
#include <QApplication>
#include <QMessageBox>
#include <algorithm>
#include <iostream>

namespace Ra {
using Core::Math::Pi;
using namespace Ra::Core::Utils;

namespace Gui {

using TrackballCameraMapping = KeyMappingManageable<TrackballCameraManipulator>;

#define KMA_VALUE( XX ) \
    Gui::KeyMappingManager::KeyMappingAction Gui::TrackballCameraManipulator::XX;
KeyMappingCamera
#undef KMA_VALUE

    void
    Gui::TrackballCameraManipulator::configureKeyMapping_impl() {

    TrackballCameraMapping::setContext(
        Gui::KeyMappingManager::getInstance()->getContext( "CameraContext" ) );
    if ( TrackballCameraMapping::getContext().isInvalid() )
    {
        LOG( logINFO )
            << "CameraContext not defined (maybe the configuration file do not contains it)";
        LOG( logERROR ) << "CameraContext all keymapping invalide !";
        return;
    }

#define KMA_VALUE( XX )                                         \
    XX = Gui::KeyMappingManager::getInstance()->getActionIndex( \
        TrackballCameraMapping::getContext(), #XX );
    KeyMappingCamera
#undef KMA_VALUE
}

Gui::TrackballCameraManipulator::TrackballCameraManipulator() :
    CameraManipulator(),
    m_rotateAround( true ),
    m_cameraRotateMode( false ),
    m_cameraPanMode( false ),
    m_cameraZoomMode( false ) {
    resetCamera();
}

Gui::TrackballCameraManipulator::TrackballCameraManipulator( const CameraManipulator& other ) :
    CameraManipulator( other ),
    m_rotateAround( true ),
    m_cameraRotateMode( false ),
    m_cameraPanMode( false ),
    m_cameraZoomMode( false ) {
    m_distFromCenter = ( m_target - m_camera->getPosition() ).norm();
    updatePhiTheta();
}

Gui::TrackballCameraManipulator::~TrackballCameraManipulator() = default;

void Gui::TrackballCameraManipulator::resetCamera() {
    m_camera->setFrame( Core::Transform::Identity() );
    m_camera->setPosition( Core::Vector3( 0_ra, 0_ra, 2_ra ) );
    m_target = Core::Vector3::Zero();
    m_camera->setDirection( Core::Vector3( 0_ra, 0_ra, -1_ra ) );
    m_distFromCenter = 2.0_ra;
    updatePhiTheta();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );
}

void Gui::TrackballCameraManipulator::updateCamera() {
    m_target         = m_camera->getPosition() + 2_ra * m_camera->getDirection().normalized();
    m_distFromCenter = 2.0_ra;
    updatePhiTheta();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );
}

void Gui::TrackballCameraManipulator::setTrackballRadius( Scalar rad ) {
    m_distFromCenter = rad;
    m_target         = m_camera->getPosition() + m_distFromCenter * m_camera->getDirection();
    updatePhiTheta();
}

Scalar Gui::TrackballCameraManipulator::getTrackballRadius() const {
    return m_distFromCenter;
}

void Gui::TrackballCameraManipulator::setTrackballCenter( const Core::Vector3& c ) {
    m_target         = c;
    m_distFromCenter = ( c - m_camera->getPosition() ).norm();
    updatePhiTheta();
}

const Core::Vector3& Gui::TrackballCameraManipulator::getTrackballCenter() const {
    return m_target;
}

bool Gui::TrackballCameraManipulator::handleMousePressEvent( QMouseEvent* event,
                                                             const Qt::MouseButtons& buttons,
                                                             const Qt::KeyboardModifiers& modifiers,
                                                             int key ) {
    bool handled = false;
    m_lastMouseX = event->pos().x();
    m_lastMouseY = event->pos().y();

    auto action = KeyMappingManager::getInstance()->getAction(
        TrackballCameraMapping::getContext(), buttons, modifiers, key, false );

    if ( action == TRACKBALLCAMERA_ROTATE )
    {
        m_cameraRotateMode = true;
        handled            = true;
    }
    if ( action == TRACKBALLCAMERA_PAN )
    {
        m_cameraPanMode = true;
        handled         = true;
    }
    if ( action == TRACKBALLCAMERA_ZOOM )
    {
        m_cameraZoomMode = true;
        handled          = true;
    }

    return handled;
}

bool Gui::TrackballCameraManipulator::handleMouseMoveEvent(
    QMouseEvent* event,
    const Qt::MouseButtons& /*buttons*/,
    const Qt::KeyboardModifiers& /* modifiers*/,
    int /*key*/ ) {

    // auto action = KeyMappingManager::getInstance()->getAction( context, buttons, modifiers, key
    // );

    Scalar dx = ( event->pos().x() - m_lastMouseX ) / m_camera->getWidth();
    Scalar dy = ( event->pos().y() - m_lastMouseY ) / m_camera->getHeight();

    if ( event->modifiers().testFlag( Qt::AltModifier ) ) { m_quickCameraModifier = 10.0_ra; }
    else
    { m_quickCameraModifier = 2.0_ra; }

    if ( m_cameraRotateMode ) { handleCameraRotate( dx, dy ); }

    if ( m_cameraPanMode ) { handleCameraPan( dx, dy ); }

    if ( m_cameraZoomMode ) { handleCameraZoom( dx, dy ); }

    m_lastMouseX = event->pos().x();
    m_lastMouseY = event->pos().y();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );

    return m_cameraRotateMode || m_cameraPanMode || m_cameraZoomMode;
}

bool Gui::TrackballCameraManipulator::handleMouseReleaseEvent( QMouseEvent* /*event*/ ) {
    m_cameraRotateMode    = false;
    m_cameraPanMode       = false;
    m_cameraZoomMode      = false;
    m_quickCameraModifier = 1.0_ra;

    return true;
}

bool Gui::TrackballCameraManipulator::handleWheelEvent( QWheelEvent* event ) {

    handleCameraZoom( ( event->angleDelta().y() * 0.01_ra + event->angleDelta().x() * 0.01_ra ) *
                      m_wheelSpeedModifier );

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraPositionChanged( m_camera->getPosition() );

    return true;
}

void Gui::TrackballCameraManipulator::toggleRotateAround() {
    m_rotateAround = !m_rotateAround;
}

bool Gui::TrackballCameraManipulator::handleKeyPressEvent(
    QKeyEvent* /*event*/,
    const KeyMappingManager::KeyMappingAction& action ) {

    if ( action == TRACKBALLCAMERA_ROTATE_AROUND )
    {
        m_rotateAround = !m_rotateAround;
        return true;
    }

    return false;
}

bool Gui::TrackballCameraManipulator::handleKeyReleaseEvent( QKeyEvent* /*e*/ ) {
    return false;
}

void Gui::TrackballCameraManipulator::setCameraPosition( const Core::Vector3& position ) {
    if ( position == m_target )
    {
        QMessageBox::warning( nullptr, "Error", "Position cannot be set to target point" );
        return;
    }
    m_camera->setPosition( position );
    /*
    m_camera->setDirection( (m_target - position).normalized() );
    m_distFromCenter = (m_target - position).norm();
    updatePhiTheta();
    */
    m_target = position + m_distFromCenter * m_camera->getDirection();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraPositionChanged( m_camera->getPosition() );
}

void Gui::TrackballCameraManipulator::setCameraTarget( const Core::Vector3& target ) {
    if ( m_camera->getPosition() == m_target )
    {
        QMessageBox::warning( nullptr, "Error", "Target cannot be set to current camera position" );
        return;
    }

    m_target = target;
    m_camera->setDirection( ( target - m_camera->getPosition() ).normalized() );
    m_distFromCenter = ( target - m_camera->getPosition() ).norm();
    updatePhiTheta();

    if ( m_light != nullptr ) { m_light->setDirection( m_camera->getDirection() ); }

    emit cameraTargetChanged( m_target );
}

void Gui::TrackballCameraManipulator::fitScene( const Core::Aabb& aabb ) {
    resetCamera();

    Scalar f = m_camera->getFOV();
    Scalar a = m_camera->getAspect();

    const Scalar r = ( aabb.max() - aabb.min() ).norm() / 2_ra;
    const Scalar x = r / std::sin( f / 2_ra );
    const Scalar y = r / std::sin( f * a / 2_ra );
    Scalar d       = std::max( std::max( x, y ), 0.001_ra );

    m_camera->setPosition(
        Core::Vector3( aabb.center().x(), aabb.center().y(), aabb.center().z() + d ) );
    m_camera->setDirection( Core::Vector3( 0_ra, 0_ra, -1_ra ) );
    m_target         = aabb.center();
    m_distFromCenter = d;

    updatePhiTheta();

    Scalar zfar = std::max( d + ( aabb.max().z() - aabb.min().z() ) * 2_ra, m_camera->getZFar() );
    m_camera->setZFar( zfar );

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );
}

void Gui::TrackballCameraManipulator::handleCameraRotate( Scalar dx, Scalar dy ) {
    Scalar dphi = dx * m_cameraSensitivity * m_quickCameraModifier;
    Scalar y    = -dy * m_cameraSensitivity * m_quickCameraModifier;

    Scalar phi   = std::fmod( m_phi + dphi, 2_ra * Pi );          // Keep phi between 0 and 2pi
    Scalar theta = std::fmod( m_theta + y + Pi, 2_ra * Pi ) - Pi; // Keep theta between -pi and pi

    Scalar dtheta = theta - m_theta;

    Core::Vector3 P =
        m_target + m_distFromCenter * Core::Vector3( -std::cos( phi ) * std::sin( theta ),
                                                     std::cos( theta ),
                                                     -std::sin( phi ) * std::sin( theta ) );

    Core::Vector3 t( P - m_camera->getPosition() );

    // Translate the camera given this translation
    Core::Transform T( Core::Transform::Identity() );
    T.translation() = t;

    // Rotate the camera so that it points to the center
    Core::Transform R1( Core::Transform::Identity() );
    Core::Transform R2( Core::Transform::Identity() );

    Core::Vector3 U = Core::Vector3( 0_ra, 1_ra, 0_ra );
    Core::Vector3 R = -m_camera->getRightVector().normalized();

    R1 = Core::AngleAxis( -dphi, U );
    R2 = Core::AngleAxis( -dtheta, R );

    m_camera->applyTransform( T * R1 * R2 );
    m_phi   = phi;
    m_theta = theta;
}

void Gui::TrackballCameraManipulator::handleCameraPan( Scalar dx, Scalar dy ) {
    Scalar x = dx * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
    Scalar y = dy * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
    // Move camera and trackball center, keep the distance to the center
    Core::Vector3 R = -m_camera->getRightVector();
    Core::Vector3 U = m_camera->getUpVector();

    Core::Transform T( Core::Transform::Identity() );
    Core::Vector3 t = x * R + y * U;
    T.translate( t );

    m_camera->applyTransform( T );
    m_target += t;
}

void Gui::TrackballCameraManipulator::handleCameraZoom( Scalar dx, Scalar dy ) {
    handleCameraZoom( Ra::Core::Math::sign( dx ) * ( std::abs( dx ) + std::abs( dy ) ) );
}

void Gui::TrackballCameraManipulator::handleCameraZoom( Scalar z ) {
    // tested this way of zooming, not convinced it's better
#if 0
    Scalar zoom = m_camera->getZoomFactor() - z * m_cameraSensitivity * m_quickCameraModifier;
    Scalar epsIn = 0.001;
    Scalar epsOut = 3.1;
    m_camera->setZoomFactor( std::clamp( zoom, epsIn, epsOut ) );
#else
    Scalar y    = m_distFromCenter * z * m_cameraSensitivity * m_quickCameraModifier;
    Scalar dist = ( m_target - m_camera->getPosition() ).norm();
    if ( dist < ( m_camera->getZNear() + y ) ) { y = dist - m_camera->getZNear(); }

    Core::Transform T( Core::Transform::Identity() );
    Core::Vector3 t = y * m_camera->getDirection();
    T.translate( t );

    m_camera->applyTransform( T );
    m_distFromCenter = ( m_target - m_camera->getPosition() ).norm();

    emit cameraPositionChanged( m_camera->getPosition() );
#endif
}

void Gui::TrackballCameraManipulator::updatePhiTheta() {
    using Core::Math::areApproxEqual;
    const auto R = m_camera->getDirection();

    m_theta = std::acos( -R.y() );

    m_phi = ( areApproxEqual( R.z(), 0_ra ) && areApproxEqual( R.x(), 0_ra ) )
                ? std::acos( m_camera->getRightVector().dot( Ra::Core::Vector3::UnitZ() ) ) + Pi
                : std::atan2( R.z(), R.x() );

    // Keep phi between 0 and 2pi
    // Keep theta between -pi and pi
    if ( m_phi < 0_ra )
    {
        m_theta = -m_theta + Pi;
        m_phi += 2_ra * Pi;
    }

    CORE_ASSERT( std::isfinite( m_theta ) && std::isfinite( m_phi ), "Error in trackball camera" );
}

bool Gui::TrackballCameraManipulator::checkIntegrity( const std::string& mess ) const {
    Core::Vector3 c = m_camera->getPosition() + m_distFromCenter * m_camera->getDirection();
    Scalar d        = ( m_target - c ).norm();
    if ( d > 0.001_ra )
    {
        LOG( logWARNING ) << "TrackballCameraManipulator Integrity problem : " << mess;
        LOG( logWARNING ) << "\t Position " << m_camera->getPosition().transpose();
        LOG( logWARNING ) << "\t Direction " << m_camera->getDirection().transpose();
        LOG( logWARNING ) << "\t Target " << m_target.transpose();
        LOG( logWARNING ) << "\t Center " << c.transpose();
        LOG( logWARNING ) << "\t Distance " << d;
    }
    return d < 0.001_ra;
}

} // namespace Gui
} // namespace Ra
<commit_msg>[gui] tentative to have better target.<commit_after>#include <Gui/Viewer/TrackballCameraManipulator.hpp>

#include <Core/Asset/Camera.hpp>
#include <Core/Math/Math.hpp>
#include <Core/Utils/Log.hpp>
#include <Engine/Scene/Light.hpp>
#include <Gui/Utils/Keyboard.hpp>

#include <Gui/Utils/KeyMappingManager.hpp>

#include <Engine/Scene/CameraComponent.hpp>
#include <QApplication>
#include <QMessageBox>
#include <algorithm>
#include <iostream>

namespace Ra {
using Core::Math::Pi;
using namespace Ra::Core::Utils;

namespace Gui {

using TrackballCameraMapping = KeyMappingManageable<TrackballCameraManipulator>;

#define KMA_VALUE( XX ) \
    Gui::KeyMappingManager::KeyMappingAction Gui::TrackballCameraManipulator::XX;
KeyMappingCamera
#undef KMA_VALUE

    void
    Gui::TrackballCameraManipulator::configureKeyMapping_impl() {

    TrackballCameraMapping::setContext(
        Gui::KeyMappingManager::getInstance()->getContext( "CameraContext" ) );
    if ( TrackballCameraMapping::getContext().isInvalid() )
    {
        LOG( logINFO )
            << "CameraContext not defined (maybe the configuration file do not contains it)";
        LOG( logERROR ) << "CameraContext all keymapping invalide !";
        return;
    }

#define KMA_VALUE( XX )                                         \
    XX = Gui::KeyMappingManager::getInstance()->getActionIndex( \
        TrackballCameraMapping::getContext(), #XX );
    KeyMappingCamera
#undef KMA_VALUE
}

Gui::TrackballCameraManipulator::TrackballCameraManipulator() :
    CameraManipulator(),
    m_rotateAround( true ),
    m_cameraRotateMode( false ),
    m_cameraPanMode( false ),
    m_cameraZoomMode( false ) {
    resetCamera();
}

Gui::TrackballCameraManipulator::TrackballCameraManipulator( const CameraManipulator& other ) :
    CameraManipulator( other ),
    m_rotateAround( true ),
    m_cameraRotateMode( false ),
    m_cameraPanMode( false ),
    m_cameraZoomMode( false ) {
    m_distFromCenter = ( m_target - m_camera->getPosition() ).norm();
    updatePhiTheta();
}

Gui::TrackballCameraManipulator::~TrackballCameraManipulator() = default;

void Gui::TrackballCameraManipulator::resetCamera() {
    m_camera->setFrame( Core::Transform::Identity() );
    m_camera->setPosition( Core::Vector3( 0_ra, 0_ra, 2_ra ) );
    m_target = Core::Vector3::Zero();
    m_camera->setDirection( Core::Vector3( 0_ra, 0_ra, -1_ra ) );
    m_distFromCenter = 2.0_ra;
    updatePhiTheta();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );
}

void Gui::TrackballCameraManipulator::updateCamera() {
    // try to keep target near the previous camera's one, take it at the same distance from camera,
    // but in the new direction.
    m_distFromCenter = ( m_target - m_camera->getPosition() ).norm();
    m_target         = m_camera->getPosition() + m_distFromCenter * m_camera->getDirection();
    updatePhiTheta();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );
}

void Gui::TrackballCameraManipulator::setTrackballRadius( Scalar rad ) {
    m_distFromCenter = rad;
    m_target         = m_camera->getPosition() + m_distFromCenter * m_camera->getDirection();
    updatePhiTheta();
}

Scalar Gui::TrackballCameraManipulator::getTrackballRadius() const {
    return m_distFromCenter;
}

void Gui::TrackballCameraManipulator::setTrackballCenter( const Core::Vector3& c ) {
    m_target         = c;
    m_distFromCenter = ( c - m_camera->getPosition() ).norm();
    updatePhiTheta();
}

const Core::Vector3& Gui::TrackballCameraManipulator::getTrackballCenter() const {
    return m_target;
}

bool Gui::TrackballCameraManipulator::handleMousePressEvent( QMouseEvent* event,
                                                             const Qt::MouseButtons& buttons,
                                                             const Qt::KeyboardModifiers& modifiers,
                                                             int key ) {
    bool handled = false;
    m_lastMouseX = event->pos().x();
    m_lastMouseY = event->pos().y();

    auto action = KeyMappingManager::getInstance()->getAction(
        TrackballCameraMapping::getContext(), buttons, modifiers, key, false );

    if ( action == TRACKBALLCAMERA_ROTATE )
    {
        m_cameraRotateMode = true;
        handled            = true;
    }
    if ( action == TRACKBALLCAMERA_PAN )
    {
        m_cameraPanMode = true;
        handled         = true;
    }
    if ( action == TRACKBALLCAMERA_ZOOM )
    {
        m_cameraZoomMode = true;
        handled          = true;
    }

    return handled;
}

bool Gui::TrackballCameraManipulator::handleMouseMoveEvent(
    QMouseEvent* event,
    const Qt::MouseButtons& /*buttons*/,
    const Qt::KeyboardModifiers& /* modifiers*/,
    int /*key*/ ) {

    // auto action = KeyMappingManager::getInstance()->getAction( context, buttons, modifiers, key
    // );

    Scalar dx = ( event->pos().x() - m_lastMouseX ) / m_camera->getWidth();
    Scalar dy = ( event->pos().y() - m_lastMouseY ) / m_camera->getHeight();

    if ( event->modifiers().testFlag( Qt::AltModifier ) ) { m_quickCameraModifier = 10.0_ra; }
    else
    { m_quickCameraModifier = 2.0_ra; }

    if ( m_cameraRotateMode ) { handleCameraRotate( dx, dy ); }

    if ( m_cameraPanMode ) { handleCameraPan( dx, dy ); }

    if ( m_cameraZoomMode ) { handleCameraZoom( dx, dy ); }

    m_lastMouseX = event->pos().x();
    m_lastMouseY = event->pos().y();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );

    return m_cameraRotateMode || m_cameraPanMode || m_cameraZoomMode;
}

bool Gui::TrackballCameraManipulator::handleMouseReleaseEvent( QMouseEvent* /*event*/ ) {
    m_cameraRotateMode    = false;
    m_cameraPanMode       = false;
    m_cameraZoomMode      = false;
    m_quickCameraModifier = 1.0_ra;

    return true;
}

bool Gui::TrackballCameraManipulator::handleWheelEvent( QWheelEvent* event ) {

    handleCameraZoom( ( event->angleDelta().y() * 0.01_ra + event->angleDelta().x() * 0.01_ra ) *
                      m_wheelSpeedModifier );

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraPositionChanged( m_camera->getPosition() );

    return true;
}

void Gui::TrackballCameraManipulator::toggleRotateAround() {
    m_rotateAround = !m_rotateAround;
}

bool Gui::TrackballCameraManipulator::handleKeyPressEvent(
    QKeyEvent* /*event*/,
    const KeyMappingManager::KeyMappingAction& action ) {

    if ( action == TRACKBALLCAMERA_ROTATE_AROUND )
    {
        m_rotateAround = !m_rotateAround;
        return true;
    }

    return false;
}

bool Gui::TrackballCameraManipulator::handleKeyReleaseEvent( QKeyEvent* /*e*/ ) {
    return false;
}

void Gui::TrackballCameraManipulator::setCameraPosition( const Core::Vector3& position ) {
    if ( position == m_target )
    {
        QMessageBox::warning( nullptr, "Error", "Position cannot be set to target point" );
        return;
    }
    m_camera->setPosition( position );
    m_target = position + m_distFromCenter * m_camera->getDirection();
    updatePhiTheta();

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraPositionChanged( m_camera->getPosition() );
}

void Gui::TrackballCameraManipulator::setCameraTarget( const Core::Vector3& target ) {
    if ( m_camera->getPosition() == m_target )
    {
        QMessageBox::warning( nullptr, "Error", "Target cannot be set to current camera position" );
        return;
    }

    m_target = target;
    m_camera->setDirection( ( target - m_camera->getPosition() ).normalized() );
    m_distFromCenter = ( target - m_camera->getPosition() ).norm();
    updatePhiTheta();

    if ( m_light != nullptr ) { m_light->setDirection( m_camera->getDirection() ); }

    emit cameraTargetChanged( m_target );
}

void Gui::TrackballCameraManipulator::fitScene( const Core::Aabb& aabb ) {
    resetCamera();

    Scalar f = m_camera->getFOV();
    Scalar a = m_camera->getAspect();

    const Scalar r = ( aabb.max() - aabb.min() ).norm() / 2_ra;
    const Scalar x = r / std::sin( f / 2_ra );
    const Scalar y = r / std::sin( f * a / 2_ra );
    Scalar d       = std::max( std::max( x, y ), 0.001_ra );

    m_camera->setPosition(
        Core::Vector3( aabb.center().x(), aabb.center().y(), aabb.center().z() + d ) );
    m_camera->setDirection( Core::Vector3( 0_ra, 0_ra, -1_ra ) );
    m_target         = aabb.center();
    m_distFromCenter = d;

    updatePhiTheta();

    Scalar zfar = std::max( d + ( aabb.max().z() - aabb.min().z() ) * 2_ra, m_camera->getZFar() );
    m_camera->setZFar( zfar );

    if ( m_light != nullptr )
    {
        m_light->setPosition( m_camera->getPosition() );
        m_light->setDirection( m_camera->getDirection() );
    }

    emit cameraChanged( m_camera->getPosition(), m_target );
}

void Gui::TrackballCameraManipulator::handleCameraRotate( Scalar dx, Scalar dy ) {
    Scalar dphi = dx * m_cameraSensitivity * m_quickCameraModifier;
    Scalar y    = -dy * m_cameraSensitivity * m_quickCameraModifier;

    Scalar phi   = std::fmod( m_phi + dphi, 2_ra * Pi );          // Keep phi between 0 and 2pi
    Scalar theta = std::fmod( m_theta + y + Pi, 2_ra * Pi ) - Pi; // Keep theta between -pi and pi

    Scalar dtheta = theta - m_theta;

    Core::Vector3 P =
        m_target + m_distFromCenter * Core::Vector3( -std::cos( phi ) * std::sin( theta ),
                                                     std::cos( theta ),
                                                     -std::sin( phi ) * std::sin( theta ) );

    Core::Vector3 t( P - m_camera->getPosition() );

    // Translate the camera given this translation
    Core::Transform T( Core::Transform::Identity() );
    T.translation() = t;

    // Rotate the camera so that it points to the center
    Core::Transform R1( Core::Transform::Identity() );
    Core::Transform R2( Core::Transform::Identity() );

    Core::Vector3 U = Core::Vector3( 0_ra, 1_ra, 0_ra );
    Core::Vector3 R = -m_camera->getRightVector().normalized();

    R1 = Core::AngleAxis( -dphi, U );
    R2 = Core::AngleAxis( -dtheta, R );

    m_camera->applyTransform( T * R1 * R2 );
    m_phi   = phi;
    m_theta = theta;
}

void Gui::TrackballCameraManipulator::handleCameraPan( Scalar dx, Scalar dy ) {
    Scalar x = dx * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
    Scalar y = dy * m_cameraSensitivity * m_quickCameraModifier * m_distFromCenter * 0.1_ra;
    // Move camera and trackball center, keep the distance to the center
    Core::Vector3 R = -m_camera->getRightVector();
    Core::Vector3 U = m_camera->getUpVector();

    Core::Transform T( Core::Transform::Identity() );
    Core::Vector3 t = x * R + y * U;
    T.translate( t );

    m_camera->applyTransform( T );
    m_target += t;
}

void Gui::TrackballCameraManipulator::handleCameraZoom( Scalar dx, Scalar dy ) {
    handleCameraZoom( Ra::Core::Math::sign( dx ) * ( std::abs( dx ) + std::abs( dy ) ) );
}

void Gui::TrackballCameraManipulator::handleCameraZoom( Scalar z ) {
    // tested this way of zooming, not convinced it's better
#if 0
    Scalar zoom = m_camera->getZoomFactor() - z * m_cameraSensitivity * m_quickCameraModifier;
    Scalar epsIn = 0.001;
    Scalar epsOut = 3.1;
    m_camera->setZoomFactor( std::clamp( zoom, epsIn, epsOut ) );
#else
    Scalar y    = m_distFromCenter * z * m_cameraSensitivity * m_quickCameraModifier;
    Scalar dist = ( m_target - m_camera->getPosition() ).norm();
    if ( dist < ( m_camera->getZNear() + y ) ) { y = dist - m_camera->getZNear(); }

    Core::Transform T( Core::Transform::Identity() );
    Core::Vector3 t = y * m_camera->getDirection();
    T.translate( t );

    m_camera->applyTransform( T );
    m_distFromCenter = ( m_target - m_camera->getPosition() ).norm();

    emit cameraPositionChanged( m_camera->getPosition() );
#endif
}

void Gui::TrackballCameraManipulator::updatePhiTheta() {
    using Core::Math::areApproxEqual;
    const auto R = m_camera->getDirection();

    m_theta = std::acos( -R.y() );

    m_phi = ( areApproxEqual( R.z(), 0_ra ) && areApproxEqual( R.x(), 0_ra ) )
                ? std::acos( m_camera->getRightVector().dot( Ra::Core::Vector3::UnitZ() ) ) + Pi
                : std::atan2( R.z(), R.x() );

    // Keep phi between 0 and 2pi
    // Keep theta between -pi and pi
    if ( m_phi < 0_ra )
    {
        m_theta = -m_theta + Pi;
        m_phi += 2_ra * Pi;
    }

    CORE_ASSERT( std::isfinite( m_theta ) && std::isfinite( m_phi ), "Error in trackball camera" );
}

bool Gui::TrackballCameraManipulator::checkIntegrity( const std::string& mess ) const {
    Core::Vector3 c = m_camera->getPosition() + m_distFromCenter * m_camera->getDirection();
    Scalar d        = ( m_target - c ).norm();
    if ( d > 0.001_ra )
    {
        LOG( logWARNING ) << "TrackballCameraManipulator Integrity problem : " << mess;
        LOG( logWARNING ) << "\t Position " << m_camera->getPosition().transpose();
        LOG( logWARNING ) << "\t Direction " << m_camera->getDirection().transpose();
        LOG( logWARNING ) << "\t Target " << m_target.transpose();
        LOG( logWARNING ) << "\t Center " << c.transpose();
        LOG( logWARNING ) << "\t Distance " << d;
    }
    return d < 0.001_ra;
}

} // namespace Gui
} // namespace Ra
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkImagePCADecompositionCalculatorTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif
// Insight classes
#include "itkImage.h"
#include "itkImageRegionIterator.h"
#include "itkLightProcessObject.h"
#include "itkTextOutput.h"

#include "itkImagePCADecompositionCalculator.h"

// class to support progress feeback

class ShowProgressObject
{
public:
  ShowProgressObject(itk::LightProcessObject * o)
    {m_Process = o;}
  void ShowProgress()
    {std::cout << "Progress " << m_Process->GetProgress() << std::endl;}
  itk::LightProcessObject::Pointer m_Process;
};

int itkImagePCADecompositionCalculatorTest(int, char* [] )
{

  itk::OutputWindow::SetInstance(itk::TextOutput::New().GetPointer());

  //Data definitions
  const unsigned int  IMGWIDTH         =  2;
  const unsigned int  IMGHEIGHT        =  2;
  const unsigned int  NDIMENSION       =  2;

  //------------------------------------------------------
  //Create 3 simple test images with
  //------------------------------------------------------
  typedef itk::Image<double,NDIMENSION> InputImageType;

  typedef
    itk::ImageRegionIterator< InputImageType > InputImageIterator;

  InputImageType::Pointer image1 = InputImageType::New();

  InputImageType::Pointer image2 = InputImageType::New();

  InputImageType::Pointer image3 = InputImageType::New();

  InputImageType::Pointer image4 = InputImageType::New();

  InputImageType::SizeType inputImageSize = {{ IMGWIDTH, IMGHEIGHT }};

  InputImageType::IndexType index;
  index.Fill(0);
  InputImageType::RegionType region;

  region.SetSize( inputImageSize );
  region.SetIndex( index );

  //--------------------------------------------------------------------------
  // Set up Image 1 first
  //--------------------------------------------------------------------------

  image1->SetRegions( region );
  image1->Allocate();

  // setup the iterators
  InputImageIterator image1It( image1, image1->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 2 first
  //--------------------------------------------------------------------------

  image2->SetRegions( region );
  image2->Allocate();

  // setup the iterators
  InputImageIterator image2It( image2, image2->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 3 first
  //--------------------------------------------------------------------------

  image3->SetRegions( region );
  image3->Allocate();

  // setup the iterators
  InputImageIterator image3It( image3, image3->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 4 first
  //--------------------------------------------------------------------------

  image4->SetRegions( region );
  image4->Allocate();

  // setup the iterators
  InputImageIterator image4It( image4, image4->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  //Manually create and store each vector
  //--------------------------------------------------------------------------
  // The first two vectors are the first two principal components of the data:
  // [1 1 1 1] , [2 0 0 2], [0 3 3 0]
  // The second two vectors are some of those data, which we will project down
  // to the PC-space
  //Image no. 1
  image1It.Set( -0.3853 ); ++image1It;
  image1It.Set( 0.5929 ); ++image1It;
  image1It.Set( 0.5929 ); ++image1It;
  image1It.Set( -0.3853 ); ++image1It;

  //Image no. 2
  image2It.Set( -0.5929 ); ++image2It;
  image2It.Set( -0.3853 ); ++image2It;
  image2It.Set( -0.3853 ); ++image2It;
  image2It.Set( -0.5929 ); ++image2It;

  //Image no. 3
  image3It.Set( 2 ); ++image3It;
  image3It.Set( 0 ); ++image3It;
  image3It.Set( 0 ); ++image3It;
  image3It.Set( 2 ); ++image3It;

  //Image no. 4
  image4It.Set( 0 ); ++image4It;
  image4It.Set( 3 ); ++image4It;
  image4It.Set( 3 ); ++image4It;
  image4It.Set( 0 ); ++image4It;

  //----------------------------------------------------------------------
  // Test code for the Decomposition Calculator
  //----------------------------------------------------------------------

  //----------------------------------------------------------------------
  //Set the image Decomposition Calculator
  //----------------------------------------------------------------------
  typedef itk::ImagePCADecompositionCalculator<InputImageType>
    ImagePCAShapeModelEstimatorType;

  ImagePCAShapeModelEstimatorType::Pointer
    decomposer = ImagePCAShapeModelEstimatorType::New();

  //----------------------------------------------------------------------
  //Set the parameters of the clusterer
  //----------------------------------------------------------------------
  // add the first two vectors to the projection basis
  ImagePCAShapeModelEstimatorType::BasisImagePointerVector basis;
  basis.push_back(image1);
  basis.push_back(image2);
  decomposer->SetBasisImages(basis);

  // compute some projections!
  ImagePCAShapeModelEstimatorType::BasisVectorType proj3, proj4;
  decomposer->SetImage(image3);
  decomposer->Compute();
  proj3 = decomposer->GetProjection();

  decomposer->SetImage(image4);
  decomposer->Compute();
  proj4 = decomposer->GetProjection();

  //Test the printself function to increase coverage
  decomposer->Print(std::cout);

  // Retrieve the basis images
  std::cout << "The basis of projection is: " << std::endl;
  ImagePCAShapeModelEstimatorType::BasisImagePointerVector basis2;
  basis2 = decomposer->GetBasisImages();
  for (ImagePCAShapeModelEstimatorType::BasisImagePointerVector::const_iterator
     basis_it = basis2.begin(); basis_it != basis2.end(); ++basis_it)
    {
    std::cout << "[";
    InputImageIterator basisImage_it( *basis_it, (*basis_it)->GetBufferedRegion() );
    for (basisImage_it.GoToBegin(); !basisImage_it.IsAtEnd(); ++basisImage_it)
      {
      std::cout << basisImage_it.Get() << " ";
      }
    std::cout << "]" << std::endl;
    }

  //Print the projections
  std::cout << "The projection of [0 2 2 0] is [" << proj3 << "]" << std::endl;
  std::cout << "this should be approx [-1.5412 -2.3716]" << std::endl;

  std::cout << "The projection of [0 3 3 0] is [" << proj4 << "]" << std::endl;
  std::cout << "this should be approx [3.5574 -2.3119]" << std::endl;

  //Test for the eigen values for the test case precomputed using Matlab
  std::cout << "" << std::endl;
  if( proj3[0] < -1.54 && proj3[0] > -1.55 && proj4[1] < -2.31 && proj4[1] > -2.32 )
    {
    std::cerr << "Test Passed" << std::endl;
    return EXIT_SUCCESS;
    }
  else
    {
    std::cerr << "Test failed" << std::endl;
    std::cerr << "The project is out of the range of Matlab precomputed values" << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;

}
<commit_msg>ENH: Improved test submitted by the class author: Zachary Pincus      http://www.itk.org/pipermail/insight-users/2004-May/008351.html<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkImagePCADecompositionCalculatorTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif
// Insight classes
#include "itkImage.h"
#include "itkImageRegionIterator.h"
#include "itkLightProcessObject.h"
#include "itkTextOutput.h"

#include "itkImagePCADecompositionCalculator.h"

// class to support progress feeback

class ShowProgressObject
{
public:
  ShowProgressObject(itk::LightProcessObject * o)
    {m_Process = o;}
  void ShowProgress()
    {std::cout << "Progress " << m_Process->GetProgress() << std::endl;}
  itk::LightProcessObject::Pointer m_Process;
};

int itkImagePCADecompositionCalculatorTest(int, char* [] )
{

  itk::OutputWindow::SetInstance(itk::TextOutput::New().GetPointer());

  //Data definitions
  const unsigned int  IMGWIDTH         =  2;
  const unsigned int  IMGHEIGHT        =  2;
  const unsigned int  NDIMENSION       =  2;
  const unsigned int  NUMTRAINIMAGES   =  3;
  const unsigned int  NUMLARGESTPC     =  2;

  //------------------------------------------------------
  //Create 3 simple test images with
  //------------------------------------------------------
  typedef itk::Image<double,NDIMENSION> InputImageType;

  typedef
    itk::ImageRegionIterator< InputImageType > InputImageIterator;

  InputImageType::Pointer image1 = InputImageType::New();

  InputImageType::Pointer image2 = InputImageType::New();

  InputImageType::Pointer image3 = InputImageType::New();

  InputImageType::Pointer image4 = InputImageType::New();

  InputImageType::Pointer image5 = InputImageType::New();

  InputImageType::Pointer image6 = InputImageType::New();

  InputImageType::Pointer image7 = InputImageType::New();

  InputImageType::SizeType inputImageSize = {{ IMGWIDTH, IMGHEIGHT }};

  InputImageType::IndexType index;
  index.Fill(0);
  InputImageType::RegionType region;

  region.SetSize( inputImageSize );
  region.SetIndex( index );

  //--------------------------------------------------------------------------
  // Set up Image 1 first
  //--------------------------------------------------------------------------

  image1->SetRegions( region );
  image1->Allocate();

  // setup the iterators
  InputImageIterator image1It( image1, image1->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 2 first
  //--------------------------------------------------------------------------

  image2->SetRegions( region );
  image2->Allocate();

  // setup the iterators
  InputImageIterator image2It( image2, image2->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 3 first
  //--------------------------------------------------------------------------

  image3->SetRegions( region );
  image3->Allocate();

  // setup the iterators
  InputImageIterator image3It( image3, image3->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 4 first
  //--------------------------------------------------------------------------

  image4->SetRegions( region );
  image4->Allocate();

  // setup the iterators
  InputImageIterator image4It( image4, image4->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 5 first
  //--------------------------------------------------------------------------

  image5->SetRegions( region );
  image5->Allocate();

  // setup the iterators
  InputImageIterator image5It( image5, image5->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 6 first
  //--------------------------------------------------------------------------

  image6->SetRegions( region );
  image6->Allocate();

  // setup the iterators
  InputImageIterator image6It( image6, image6->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  // Set up Image 7 first
  //--------------------------------------------------------------------------

  image7->SetRegions( region );
  image7->Allocate();

  // setup the iterators
  InputImageIterator image7It( image7, image7->GetBufferedRegion() );

  //--------------------------------------------------------------------------
  //Manually create and store each vector
  //--------------------------------------------------------------------------
  // The first two vectors are the first two principal components of the data:
  // [1 1 1 1] , [2 0 0 2], [0 3 3 0]
  // The second two vectors are some of those data, which we will project down
  // to the PC-space
  // The last three vectors are a new basis set to projet down into, so we can
  // test changing bases mid-stream.

  //Image no. 1
  image1It.Set( -0.3853 ); ++image1It;
  image1It.Set( 0.5929 ); ++image1It;
  image1It.Set( 0.5929 ); ++image1It;
  image1It.Set( -0.3853 ); ++image1It;

  //Image no. 2
  image2It.Set( -0.5929 ); ++image2It;
  image2It.Set( -0.3853 ); ++image2It;
  image2It.Set( -0.3853 ); ++image2It;
  image2It.Set( -0.5929 ); ++image2It;

  //Image no. 3
  image3It.Set( 2 ); ++image3It;
  image3It.Set( 0 ); ++image3It;
  image3It.Set( 0 ); ++image3It;
  image3It.Set( 2 ); ++image3It;

  //Image no. 4
  image4It.Set( 0 ); ++image4It;
  image4It.Set( 3 ); ++image4It;
  image4It.Set( 3 ); ++image4It;
  image4It.Set( 0 ); ++image4It;

  //Image no. 5
  image5It.Set( 0.70710678 ); ++image5It;  // 1/sqrt(2)
  image5It.Set( 0.70710678 ); ++image5It;
  image5It.Set( 0 ); ++image5It;
  image5It.Set( 0 ); ++image5It;

  //Image no. 6
  image6It.Set( -0.70710678 ); ++image6It;
  image6It.Set( 0.70710678 ); ++image6It;
  image6It.Set( 0 ); ++image6It;
  image6It.Set( 0 ); ++image6It;

  //Image no. 7
  image7It.Set( 0 ); ++image7It;
  image7It.Set( 0 ); ++image7It;
  image7It.Set( 1 ); ++image7It;
  image7It.Set( 0 ); ++image7It;

  //----------------------------------------------------------------------
  // Test code for the Decomposition Calculator
  //----------------------------------------------------------------------

  //----------------------------------------------------------------------
  //Set the image Decomposition Calculator
  //----------------------------------------------------------------------
  typedef itk::ImagePCADecompositionCalculator<InputImageType>
    ImagePCAShapeModelEstimatorType;

  ImagePCAShapeModelEstimatorType::Pointer
    decomposer = ImagePCAShapeModelEstimatorType::New();

  //----------------------------------------------------------------------
  //Set the parameters of the clusterer
  //----------------------------------------------------------------------
  // add the first two vectors to the projection basis
  ImagePCAShapeModelEstimatorType::BasisImagePointerVector basis;
  basis.push_back(image1);
  basis.push_back(image2);
  decomposer->SetBasisImages(basis);

  // compute some projections!
  ImagePCAShapeModelEstimatorType::BasisVectorType proj3, proj4;
  decomposer->SetImage(image3);
  decomposer->Compute();
  proj3 = decomposer->GetProjection();

  decomposer->SetImage(image4);
  decomposer->Compute();
  proj4 = decomposer->GetProjection();

  // get the basis images
  ImagePCAShapeModelEstimatorType::BasisImagePointerVector basis_check;
  basis_check = decomposer->GetBasisImages();

  basis.clear();
  basis.push_back(image5);
  basis.push_back(image6);
  basis.push_back(image7);
  decomposer->SetBasisImages(basis);

  ImagePCAShapeModelEstimatorType::BasisVectorType proj3_2, proj4_2;
  //decomposer->SetImage(image4); // DON'T set image4 -- it should still
  // be cached with the decomposer. Test that this works between basis changes.
  decomposer->Compute();
  proj4_2 = decomposer->GetProjection();

  decomposer->SetImage(image3);
  decomposer->Compute();
  proj3_2 = decomposer->GetProjection();

  // get the basis images
  ImagePCAShapeModelEstimatorType::BasisImagePointerVector basis_check_2;
  basis_check_2 = decomposer->GetBasisImages();

  //Test the printself function to increase coverage
  decomposer->Print(std::cout);

  // Print the basis and projections: first the PCA basis
  std::cout << "The basis of projection is: " << std::endl;
  for (ImagePCAShapeModelEstimatorType::BasisImagePointerVector::const_iterator
     basis_it = basis_check.begin(); basis_it != basis_check.end(); ++basis_it)
    {
    std::cout << "[";
    InputImageIterator basisImage_it( *basis_it, (*basis_it)->GetBufferedRegion() );
    for (basisImage_it.GoToBegin(); !basisImage_it.IsAtEnd(); ++basisImage_it)
      {
      std::cout << basisImage_it.Get() << " ";
      }
    std::cout << "]" << std::endl;
    }

  //Print the projections
  std::cout << "The projection of [0 2 2 0] is [" << proj3 << "]" << std::endl;
  std::cout << "this should be approx [-1.5412 -2.3716]" << std::endl;

  std::cout << "The projection of [0 3 3 0] is [" << proj4 << "]" << std::endl;
  std::cout << "this should be approx [3.5574 -2.3119]" << std::endl;

  // Print the basis and projections: now the new basis
  std::cout << std::endl;
  std::cout << "Now the basis of projection is: " << std::endl;
  for (ImagePCAShapeModelEstimatorType::BasisImagePointerVector::const_iterator
     basis_it = basis_check_2.begin(); basis_it != basis_check_2.end(); ++basis_it)
    {
    std::cout << "[";
    InputImageIterator basisImage_it( *basis_it, (*basis_it)->GetBufferedRegion() );
    for (basisImage_it.GoToBegin(); !basisImage_it.IsAtEnd(); ++basisImage_it)
      {
      std::cout << basisImage_it.Get() << " ";
      }
    std::cout << "]" << std::endl;
    }

  //Print the projections
  std::cout << "The projection of [0 2 2 0] is [" << proj3_2 << "]" << std::endl;
  std::cout << "this should be approx [1.4142 -1.4142 0]" << std::endl;

  std::cout << "The projection of [0 3 3 0] is [" << proj4_2 << "]" << std::endl;
  std::cout << "this should be approx [2.1213 2.1213 3.000]" << std::endl;

  //Test for the eigen values for the test case precomputed using Matlab
  std::cout << "" << std::endl;
  if( proj3[0] < -1.54 && proj3[0] > -1.55 && proj4[1] < -2.31 && proj4[1] > -2.32 &&
      proj3_2[1] < -1.414 && proj3_2[1] > -1.415 && proj4_2[2] < 3.01 && proj4_2[2] > 2.99 )
    {
    std::cerr << "Test Passed" << std::endl;
    return EXIT_SUCCESS;
    }
  else
    {
    std::cerr << "Test failed" << std::endl;
    std::cerr << "The project is out of the range of Matlab precomputed values" << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;

}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: converterbase.cxx,v $
 *
 * $Revision: 1.3 $
 *
 * 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
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "oox/drawingml/chart/converterbase.hxx"
#include "oox/drawingml/theme.hxx"
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <comphelper/processfactory.hxx>

using ::rtl::OUString;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::XInterface;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::lang::XMultiServiceFactory;
using ::com::sun::star::frame::XModel;
using ::com::sun::star::chart2::XChartDocument;
using ::oox::core::XmlFilterBase;

namespace oox {
namespace drawingml {
namespace chart {

// ============================================================================

namespace {

/** Enumerates different sets of property names for chart object formatting. */
enum PropertyType
{
    PROPERTYTYPE_COMMON,                /// Common objects, no special handling.
    PROPERTYTYPE_LINEARSERIES,          /// Specific to linear data series.
    PROPERTYTYPE_FILLEDSERIES           /// Specific to filled data series.
};

// ============================================================================

enum AutoColorType
{
    AUTOCOLORTYPE_NONE,         /// No color.
    AUTOCOLORTYPE_RGB,          /// RGB color.
    AUTOCOLORTYPE_THEME         /// Theme color.
};

struct AutoFormatEntry
{
    sal_Int32           mnFirstStyleIdx;    /// First chart style index.
    sal_Int32           mnLastStyleIdx;     /// Last chart style index.
    sal_Int32           mnThemeIdx;         /// Themed style index.
    AutoColorType       meColorType;        /// Type of the color value.
    sal_Int32           mnColorValue;       /// Color value (dependent on type).
    sal_Int32           mnModToken;         /// Color modification token.
    sal_Int32           mnModValue;         /// Color modification value.
};

// ----------------------------------------------------------------------------

#define AUTOFORMAT_THEME_COL( first, last, theme_idx, color_token ) \
    { first, last, theme_idx, AUTOCOLORTYPE_THEME, color_token, XML_TOKEN_INVALID, 0 }

#define AUTOFORMAT_THEME_MOD( first, last, theme_idx, color_token, mod_token, mod_value ) \
    { first, last, theme_idx, AUTOCOLORTYPE_THEME, color_token, mod_token, mod_value }

#define AUTOFORMAT_NONE( first, last ) \
    { first, last, -1, AUTOCOLORTYPE_NONE, 0, XML_TOKEN_INVALID, 0 }

#define AUTOFORMAT_END() \
    AUTOFORMAT_NONE( -1, -1 )

static const AutoFormatEntry spNoFormats[] =
{
    AUTOFORMAT_NONE( 1, 48 ),
    AUTOFORMAT_END()
};

static const AutoFormatEntry spChartSpaceLines[] =
{
    AUTOFORMAT_THEME_MOD(  1, 32, THEMED_INDEX_SUBTLE, XML_tx1, XML_tint, 75000 ),
    AUTOFORMAT_THEME_MOD( 33, 40, THEMED_INDEX_SUBTLE, XML_dk1, XML_tint, 75000 ),
    // 41...48: no line, same as Chart2
    AUTOFORMAT_END()
};

static const AutoFormatEntry spChartSpaceFills[] =
{
    AUTOFORMAT_THEME_COL(  1, 32, THEMED_INDEX_SUBTLE, XML_bg1 ),
    AUTOFORMAT_THEME_COL( 33, 40, THEMED_INDEX_SUBTLE, XML_lt1 ),
    AUTOFORMAT_THEME_COL( 41, 48, THEMED_INDEX_SUBTLE, XML_dk1 ),
    AUTOFORMAT_END()
};

static const AutoFormatEntry spPlotArea2dFills[] =
{
    AUTOFORMAT_THEME_COL(  1, 32, THEMED_INDEX_SUBTLE, XML_bg1 ),
    AUTOFORMAT_THEME_MOD( 33, 34, THEMED_INDEX_SUBTLE, XML_dk1, XML_tint, 20000 ),
    AUTOFORMAT_THEME_COL( 35, 35, THEMED_INDEX_SUBTLE, XML_accent1 ),
    AUTOFORMAT_THEME_COL( 36, 36, THEMED_INDEX_SUBTLE, XML_accent2 ),
    AUTOFORMAT_THEME_COL( 37, 37, THEMED_INDEX_SUBTLE, XML_accent3 ),
    AUTOFORMAT_THEME_COL( 38, 38, THEMED_INDEX_SUBTLE, XML_accent4 ),
    AUTOFORMAT_THEME_COL( 39, 39, THEMED_INDEX_SUBTLE, XML_accent5 ),
    AUTOFORMAT_THEME_COL( 40, 40, THEMED_INDEX_SUBTLE, XML_accent6 ),
    AUTOFORMAT_THEME_MOD( 41, 48, THEMED_INDEX_SUBTLE, XML_dk1, XML_tint, 95000 ),
    AUTOFORMAT_END()
};

static const AutoFormatEntry spWallFloorFills[] =
{
    AUTOFORMAT_NONE(       1, 32 ),
    AUTOFORMAT_THEME_MOD( 33, 34, THEMED_INDEX_SUBTLE, XML_dk1, XML_tint, 20000 ),
    AUTOFORMAT_THEME_COL( 35, 35, THEMED_INDEX_SUBTLE, XML_accent1 ),
    AUTOFORMAT_THEME_COL( 36, 36, THEMED_INDEX_SUBTLE, XML_accent2 ),
    AUTOFORMAT_THEME_COL( 37, 37, THEMED_INDEX_SUBTLE, XML_accent3 ),
    AUTOFORMAT_THEME_COL( 38, 38, THEMED_INDEX_SUBTLE, XML_accent4 ),
    AUTOFORMAT_THEME_COL( 39, 39, THEMED_INDEX_SUBTLE, XML_accent5 ),
    AUTOFORMAT_THEME_COL( 40, 40, THEMED_INDEX_SUBTLE, XML_accent6 ),
    AUTOFORMAT_THEME_MOD( 41, 48, THEMED_INDEX_SUBTLE, XML_dk1, XML_tint, 95000 ),
    AUTOFORMAT_END()
};

#undef AUTOFORMAT_THEME_COL
#undef AUTOFORMAT_THEME_MOD
#undef AUTOFORMAT_NONE
#undef AUTOFORMAT_END

const AutoFormatEntry* lclGetEntry( const AutoFormatEntry* pEntries, sal_Int32 nStyle )
{
    for( ; pEntries && (pEntries->mnFirstStyleIdx >= 0); ++pEntries )
        if( (pEntries->mnFirstStyleIdx <= nStyle) && (nStyle <= pEntries->mnLastStyleIdx) )
            return pEntries;
    return 0;
}

// ----------------------------------------------------------------------------

/** Contains information about auto formatting of a specific chart object type. */
struct ObjectAutoFormatEntry
{
    ObjectType          meObjType;          /// Object type for automatic format.
    PropertyType        mePropType;         /// Property type for property names.
    const AutoFormatEntry* mpLines;          /// Automatic line formatting for all chart styles.
    const AutoFormatEntry* mpFills;          /// Automatic fill formatting for all chart styles.
    const AutoFormatEntry* mpEffects;        /// Automatic effects for all chart styles.
    bool                mbIsFrame;          /// True = object is a frame, false = object is a line.
};

// ----------------------------------------------------------------------------

static const ObjectAutoFormatEntry spObjAutoFormats[] =
{
    // object type               property type              auto line           auto fill           auto effect         isframe
    { OBJECTTYPE_CHARTSPACE,     PROPERTYTYPE_COMMON,       spChartSpaceLines,  spChartSpaceFills,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_CHARTTITLE,     PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_LEGEND,         PROPERTYTYPE_COMMON,       spNoFormats,        spNoFormats,        0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_PLOTAREA2D,     PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  spPlotArea2dFills,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_PLOTAREA3D,     PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_WALL,           PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  spWallFloorFills,   0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_FLOOR,          PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  spWallFloorFills,   0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_AXIS,           PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_AXISTITLE,      PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_AXISUNIT,       PROPERTYTYPE_COMMON,       0,                  0,                  0,                  true  },
    { OBJECTTYPE_GRIDLINE,       PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_LINEARSERIES2D, PROPERTYTYPE_LINEARSERIES, 0,                  0,                  0,                  false },
    { OBJECTTYPE_FILLEDSERIES2D, PROPERTYTYPE_FILLEDSERIES, 0,                  0,                  0,                  true  },
    { OBJECTTYPE_FILLEDSERIES3D, PROPERTYTYPE_FILLEDSERIES, 0,                  0,                  0,                  true  },
    { OBJECTTYPE_DATALABEL,      PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_TRENDLINE,      PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_TRENDLINELABEL, PROPERTYTYPE_COMMON,       0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  0 /* eq to Ch2 */,  true  },
    { OBJECTTYPE_ERRORBAR,       PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_SERLINE,        PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_LEADERLINE,     PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_DROPLINE,       PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
    { OBJECTTYPE_HILOLINE,       PROPERTYTYPE_LINEARSERIES, 0,                  0,                  0,                  false },
    { OBJECTTYPE_UPBAR,          PROPERTYTYPE_COMMON,       0,                  0,                  0,                  true  },
    { OBJECTTYPE_DOWNBAR,        PROPERTYTYPE_COMMON,       0,                  0,                  0,                  true  },
    { OBJECTTYPE_DATATABLE,      PROPERTYTYPE_COMMON,       0,                  0,                  0,                  false },
};

// ============================================================================

/** Auto formatting for a specific object type. */
class ObjectAutoFormat
{
public:
    explicit            ObjectAutoFormat(
                            const XmlFilterBase& rFilter,
                            const ObjectAutoFormatEntry& rObjFmt,
                            sal_Int32 nStyle );

    /** Sets auto formatting properties to the passed property set. */
    void                convertAutoFormats( PropertySet& rPropSet ) const;

private:
    struct AutoFormatInfo
    {
        enum AutoFormatType { AUTOFORMAT_SKIP, AUTOFORMAT_INVISIBLE, AUTOFORMAT_EXPLICIT };

        AutoFormatType      meType;             /// Type of the auto formatting.
        sal_Int32           mnThemeIdx;         /// Themed style index.
        sal_Int32           mnColor;            /// RGB color used for themed style.

        explicit            AutoFormatInfo( const XmlFilterBase& rFilter, const AutoFormatEntry* pEntry );
    };

    AutoFormatInfo      maLineFmt;          /// Automatic line formatting.
    AutoFormatInfo      maFillFmt;          /// Automatic fill formatting.
    AutoFormatInfo      maEffectFmt;        /// Automatic effect formatting.
    PropertyType        mePropType;         /// Property type for property names.
    bool                mbIsFrame;          /// True = object is a frame, false = object is a line.
};

ObjectAutoFormat::AutoFormatInfo::AutoFormatInfo( const XmlFilterBase& rFilter, const AutoFormatEntry* pEntry ) :
    meType( AUTOFORMAT_SKIP ),
    mnThemeIdx( -1 ),
    mnColor( -1 )
{
    if( pEntry )
    {
        meType = (pEntry->meColorType == AUTOCOLORTYPE_NONE) ? AUTOFORMAT_INVISIBLE : AUTOFORMAT_EXPLICIT;
        mnThemeIdx = pEntry->mnThemeIdx;

        Color aColor;
        switch( pEntry->meColorType )
        {
        case AUTOCOLORTYPE_NONE:
            break;
            case AUTOCOLORTYPE_RGB:
                aColor.setSrgbClr( pEntry->mnColorValue );
            break;
            case AUTOCOLORTYPE_THEME:
                aColor.setSchemeClr( pEntry->mnColorValue );
            break;
        }
        if( pEntry->mnModToken != XML_TOKEN_INVALID )
            aColor.addTransformation( pEntry->mnModToken, pEntry->mnModValue );
        mnColor = aColor.getColor( rFilter );
    }
}

ObjectAutoFormat::ObjectAutoFormat( const XmlFilterBase& rFilter, const ObjectAutoFormatEntry& rObjFmt, sal_Int32 nStyle ) :
    maLineFmt( rFilter, lclGetEntry( rObjFmt.mpLines, nStyle ) ),
    maFillFmt( rFilter, lclGetEntry( rObjFmt.mpFills, nStyle ) ),
    maEffectFmt( rFilter, lclGetEntry( rObjFmt.mpEffects, nStyle ) ),
    mePropType( rObjFmt.mePropType ),
    mbIsFrame( rObjFmt.mbIsFrame )
{
}

void ObjectAutoFormat::convertAutoFormats( PropertySet& rPropSet ) const
{
    namespace cssd = ::com::sun::star::drawing;

    switch( maLineFmt.meType )
    {
        case AutoFormatInfo::AUTOFORMAT_SKIP:
        break;
        case AutoFormatInfo::AUTOFORMAT_INVISIBLE:
            rPropSet.setProperty( CREATE_OUSTRING( "LineStyle" ), cssd::LineStyle_NONE );
        break;
        case AutoFormatInfo::AUTOFORMAT_EXPLICIT:
            rPropSet.setProperty( CREATE_OUSTRING( "LineStyle" ), cssd::LineStyle_SOLID );
            rPropSet.setProperty( CREATE_OUSTRING( "LineWidth" ), sal_Int32( 0 ) ); // hairline
            rPropSet.setProperty( CREATE_OUSTRING( "LineColor" ), maLineFmt.mnColor );
        break;
    }

    switch( maFillFmt.meType )
    {
        case AutoFormatInfo::AUTOFORMAT_SKIP:
        break;
        case AutoFormatInfo::AUTOFORMAT_INVISIBLE:
            rPropSet.setProperty( CREATE_OUSTRING( "FillStyle" ), cssd::FillStyle_NONE );
        break;
        case AutoFormatInfo::AUTOFORMAT_EXPLICIT:
            rPropSet.setProperty( CREATE_OUSTRING( "FillStyle" ), cssd::FillStyle_SOLID );
            rPropSet.setProperty( CREATE_OUSTRING( "FillColor" ), maFillFmt.mnColor );
        break;
    }
}

} // namespace

// ============================================================================
// ============================================================================

struct ConverterData
{
    typedef RefMap< ObjectType, ObjectAutoFormat > ObjectAutoFormatMap;

    XmlFilterBase&      mrFilter;
    ChartConverter&     mrConverter;
    Reference< XChartDocument > mxDoc;
    ObjectAutoFormatMap maAutoFormats;

    explicit            ConverterData(
                            XmlFilterBase& rFilter,
                            ChartConverter& rChartConverter,
                            const Reference< XChartDocument >& rxChartDoc );
                        ~ConverterData();
};

// ----------------------------------------------------------------------------

ConverterData::ConverterData(
        XmlFilterBase& rFilter,
        ChartConverter& rChartConverter,
        const Reference< XChartDocument >& rxChartDoc ) :
    mrFilter( rFilter ),
    mrConverter( rChartConverter ),
    mxDoc( rxChartDoc )
{
    OSL_ENSURE( mxDoc.is(), "ConverterData::ConverterData - missing chart document" );
    // lock the model to suppress internal updates during conversion
    try
    {
        Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
        xModel->lockControllers();
    }
    catch( Exception& )
    {
    }
}

ConverterData::~ConverterData()
{
    // unlock the model
    try
    {
        Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
        xModel->unlockControllers();
    }
    catch( Exception& )
    {
    }
}

// ============================================================================

ConverterRoot::ConverterRoot(
        XmlFilterBase& rFilter,
        ChartConverter& rChartConverter,
        const Reference< XChartDocument >& rxChartDoc ) :
    mxData( new ConverterData( rFilter, rChartConverter, rxChartDoc ) )
{
}

ConverterRoot::~ConverterRoot()
{
}

Reference< XInterface > ConverterRoot::createInstance(
        const Reference< XMultiServiceFactory >& rxFactory, const OUString& rServiceName )
{
    Reference< XInterface > xInt;
    if( rxFactory.is() ) try
    {
        xInt = rxFactory->createInstance( rServiceName );
    }
    catch( Exception& )
    {
    }
    OSL_ENSURE( xInt.is(), "ConverterRoot::createInstance - cannot create instance" );
    return xInt;
}

Reference< XInterface > ConverterRoot::createInstance( const OUString& rServiceName )
{
    return createInstance( ::comphelper::getProcessServiceFactory(), rServiceName );
}

XmlFilterBase& ConverterRoot::getFilter() const
{
    return mxData->mrFilter;
}

ChartConverter& ConverterRoot::getChartConverter() const
{
    return mxData->mrConverter;
}

Reference< XChartDocument > ConverterRoot::getChartDocument() const
{
    return mxData->mxDoc;
}

void ConverterRoot::initAutoFormats( sal_Int32 nStyle )
{
    const ObjectAutoFormatEntry* pObjFmtEnd = STATIC_ARRAY_END( spObjAutoFormats );
    for( const ObjectAutoFormatEntry* pObjFmt = spObjAutoFormats; pObjFmt != pObjFmtEnd; ++pObjFmt )
        mxData->maAutoFormats[ pObjFmt->meObjType ].reset( new ObjectAutoFormat( mxData->mrFilter, *pObjFmt, nStyle ) );
}

void ConverterRoot::convertAutoFormats( PropertySet& rPropSet, ObjectType eObjType ) const
{
    if( const ObjectAutoFormat* pAutoFormat = mxData->maAutoFormats.get( eObjType ).get() )
        pAutoFormat->convertAutoFormats( rPropSet );
}

// ============================================================================

} // namespace chart
} // namespace drawingml
} // namespace oox

<commit_msg>INTEGRATION: CWS xmlfilter06 (1.2.6); FILE MERGED 2008/07/04 11:46:57 dr 1.2.6.7: #i10000# resync errors 2008/07/04 11:03:41 dr 1.2.6.6: RESYNC: (1.2-1.3); FILE MERGED 2008/06/23 12:55:46 dr 1.2.6.5: final chart font formatting 2008/06/09 15:16:12 dr 1.2.6.4: more chart line formatting, add line dashs to chart dash container 2008/06/06 16:27:53 dr 1.2.6.3: chart formatting: builtin line/fill/effect styles, manual line formatting 2008/05/27 15:00:36 dr 1.2.6.2: #i10000# 2008/05/27 10:40:38 dr 1.2.6.1: joined changes from CWS xmlfilter05<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: converterbase.cxx,v $
 *
 * $Revision: 1.4 $
 *
 * 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
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "oox/drawingml/chart/converterbase.hxx"
#include "oox/drawingml/theme.hxx"
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <comphelper/processfactory.hxx>

using ::rtl::OUString;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::XInterface;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::lang::XMultiServiceFactory;
using ::com::sun::star::frame::XModel;
using ::com::sun::star::chart2::XChartDocument;
using ::oox::core::XmlFilterBase;

namespace oox {
namespace drawingml {
namespace chart {

// ============================================================================

struct ConverterData
{
    XmlFilterBase&      mrFilter;
    ChartConverter&     mrConverter;
    Reference< XChartDocument > mxDoc;
    ObjectFormatter     maFormatter;

    explicit            ConverterData(
                            XmlFilterBase& rFilter,
                            ChartConverter& rChartConverter,
                            const Reference< XChartDocument >& rxChartDoc,
                            const ChartSpaceModel& rChartSpace );
                        ~ConverterData();
};

// ----------------------------------------------------------------------------

ConverterData::ConverterData(
        XmlFilterBase& rFilter,
        ChartConverter& rChartConverter,
        const Reference< XChartDocument >& rxChartDoc,
        const ChartSpaceModel& rChartSpace ) :
    mrFilter( rFilter ),
    mrConverter( rChartConverter ),
    mxDoc( rxChartDoc ),
    maFormatter( rFilter, rxChartDoc, rChartSpace )
{
    OSL_ENSURE( mxDoc.is(), "ConverterData::ConverterData - missing chart document" );
    // lock the model to suppress internal updates during conversion
    try
    {
        Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
        xModel->lockControllers();
    }
    catch( Exception& )
    {
    }
}

ConverterData::~ConverterData()
{
    // unlock the model
    try
    {
        Reference< XModel > xModel( mxDoc, UNO_QUERY_THROW );
        xModel->unlockControllers();
    }
    catch( Exception& )
    {
    }
}

// ============================================================================

ConverterRoot::ConverterRoot(
        XmlFilterBase& rFilter,
        ChartConverter& rChartConverter,
        const Reference< XChartDocument >& rxChartDoc,
        const ChartSpaceModel& rChartSpace ) :
    mxData( new ConverterData( rFilter, rChartConverter, rxChartDoc, rChartSpace ) )
{
}

ConverterRoot::~ConverterRoot()
{
}

Reference< XInterface > ConverterRoot::createInstance(
        const Reference< XMultiServiceFactory >& rxFactory, const OUString& rServiceName )
{
    Reference< XInterface > xInt;
    if( rxFactory.is() ) try
    {
        xInt = rxFactory->createInstance( rServiceName );
    }
    catch( Exception& )
    {
    }
    OSL_ENSURE( xInt.is(), "ConverterRoot::createInstance - cannot create instance" );
    return xInt;
}

Reference< XInterface > ConverterRoot::createInstance( const OUString& rServiceName )
{
    return createInstance( ::comphelper::getProcessServiceFactory(), rServiceName );
}

XmlFilterBase& ConverterRoot::getFilter() const
{
    return mxData->mrFilter;
}

ChartConverter& ConverterRoot::getChartConverter() const
{
    return mxData->mrConverter;
}

Reference< XChartDocument > ConverterRoot::getChartDocument() const
{
    return mxData->mxDoc;
}

ObjectFormatter& ConverterRoot::getFormatter() const
{
    return mxData->maFormatter;
}

// ============================================================================

} // namespace chart
} // namespace drawingml
} // namespace oox

<|endoftext|>


<commit_before><commit_msg>Fix typo introduced in r56164. This caused PLT histograms to only be saved in abandonment cases.<commit_after><|endoftext|>


<commit_before><commit_msg>Updated to pass coding policy checks<commit_after><|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"

#ifndef _PreComp_
#endif

#include "ViewProviderAddSub.h"
#include "Mod/Part/Gui/SoBrepFaceSet.h"
#include <Mod/PartDesign/App/FeatureAddSub.h>
#include <Gui/TaskView/TaskDialog.h>
#include <Gui/Control.h>
#include <Gui/Command.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Document.h>
#include <Base/Console.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoNormal.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Bnd_Box.hxx>
#include <BRepBndLib.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRep_Tool.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>

using namespace PartDesignGui;

PROPERTY_SOURCE(PartDesignGui::ViewProviderAddSub,PartDesignGui::ViewProvider)

ViewProviderAddSub::ViewProviderAddSub()
{

    previewShape = new SoSeparator();
    previewShape->ref();
    previewFaceSet = new PartGui::SoBrepFaceSet();
    previewFaceSet->ref();
    previewCoords = new SoCoordinate3();
    previewCoords->ref();
    previewNorm = new SoNormal();
    previewNorm->ref();

}

ViewProviderAddSub::~ViewProviderAddSub()
{
    previewFaceSet->unref();
    previewCoords->unref();
    previewNorm->unref();
    previewShape->unref();
}

void ViewProviderAddSub::attach(App::DocumentObject* obj) {

    ViewProvider::attach(obj);

    auto* bind = new SoMaterialBinding();
    bind->value = SoMaterialBinding::OVERALL;
    auto* material = new SoMaterial();
    if(static_cast<PartDesign::FeatureAddSub*>(obj)->getAddSubType() == PartDesign::FeatureAddSub::Additive)
        material->diffuseColor = SbColor(1,1,0);
    else
        material->diffuseColor = SbColor(1,0,0);

    material->transparency = 0.7f;
    auto* pick = new SoPickStyle();
    pick->style = SoPickStyle::UNPICKABLE;

    previewShape->addChild(pick);
    previewShape->addChild(bind);
    previewShape->addChild(material);
    previewShape->addChild(previewCoords);
    previewShape->addChild(previewNorm);
    previewShape->addChild(previewFaceSet);

    addDisplayMaskMode(previewShape, "Shape preview");
    updateAddSubShapeIndicator();
}

void ViewProviderAddSub::updateAddSubShapeIndicator() {

    TopoDS_Shape cShape(static_cast<PartDesign::FeatureAddSub*>(getObject())->AddSubShape.getValue());
    if (cShape.IsNull()) {
        previewCoords  ->point      .setNum(0);
        previewNorm    ->vector     .setNum(0);
        previewFaceSet ->coordIndex .setNum(0);
        previewFaceSet ->partIndex  .setNum(0);
        return;
    }

    int numTriangles=0,numNodes=0,numNorms=0,numFaces=0;
    std::set<int> faceEdges;

    try {
        // calculating the deflection value
        Bnd_Box bounds;
        BRepBndLib::Add(cShape, bounds);
        bounds.SetGap(0.0);
        Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
        bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
        Standard_Real deflection = ((xMax-xMin)+(yMax-yMin)+(zMax-zMin))/300.0 *
            Deviation.getValue();

        // create or use the mesh on the data structure
#if OCC_VERSION_HEX >= 0x060600
        BRepMesh_IncrementalMesh(cShape,deflection,Standard_False,
                deflection,Standard_True);
#else
        BRepMesh_IncrementalMesh(cShape,deflection);
#endif
        // We must reset the location here because the transformation data
        // are set in the placement property
        TopLoc_Location aLoc;
        cShape.Location(aLoc);

        // count triangles and nodes in the mesh
        TopExp_Explorer Ex;
        for (Ex.Init(cShape,TopAbs_FACE);Ex.More();Ex.Next()) {
            Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(Ex.Current()), aLoc);
            // Note: we must also count empty faces
            if (!mesh.IsNull()) {
                numTriangles += mesh->NbTriangles();
                numNodes     += mesh->NbNodes();
                numNorms     += mesh->NbNodes();
            }
            numFaces++;
        }

        // create memory for the nodes and indexes
        previewCoords  ->point      .setNum(numNodes);
        previewNorm    ->vector     .setNum(numNorms);
        previewFaceSet ->coordIndex .setNum(numTriangles*4);
        previewFaceSet ->partIndex  .setNum(numFaces);
        // get the raw memory for fast fill up
        SbVec3f* verts = previewCoords  ->point       .startEditing();
        SbVec3f* previewNorms = previewNorm    ->vector      .startEditing();
        int32_t* index = previewFaceSet ->coordIndex  .startEditing();
        int32_t* parts = previewFaceSet ->partIndex   .startEditing();

        // preset the previewNormal vector with null vector
        for (int i=0;i < numNorms;i++)
            previewNorms[i]= SbVec3f(0.0,0.0,0.0);

        int ii = 0,faceNodeOffset=0,faceTriaOffset=0;
        for (Ex.Init(cShape, TopAbs_FACE); Ex.More(); Ex.Next(),ii++) {
            TopLoc_Location aLoc;
            const TopoDS_Face &actFace = TopoDS::Face(Ex.Current());
            // get the mesh of the shape
            Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(actFace,aLoc);
            if (mesh.IsNull()) continue;

            // getting the transformation of the shape/face
            gp_Trsf myTransf;
            Standard_Boolean identity = true;
            if (!aLoc.IsIdentity()) {
                identity = false;
                myTransf = aLoc.Transformation();
            }

            // getting size of node and triangle array of this face
            int nbNodesInFace = mesh->NbNodes();
            int nbTriInFace   = mesh->NbTriangles();
            // check orientation
            TopAbs_Orientation orient = actFace.Orientation();

            // cycling through the poly mesh
            const Poly_Array1OfTriangle& Triangles = mesh->Triangles();
            const TColgp_Array1OfPnt& Nodes = mesh->Nodes();
            TColgp_Array1OfDir Normals (Nodes.Lower(), Nodes.Upper());
            GetNormals(actFace, mesh, Normals);

            for (int g=1;g<=nbTriInFace;g++) {
                // Get the triangle
                Standard_Integer N1,N2,N3;
                Triangles(g).Get(N1,N2,N3);

                // change orientation of the triangle if the face is reversed
                if ( orient != TopAbs_FORWARD ) {
                    Standard_Integer tmp = N1;
                    N1 = N2;
                    N2 = tmp;
                }

                // get the 3 points of this triangle
                gp_Pnt V1(Nodes(N1)), V2(Nodes(N2)), V3(Nodes(N3));

                // get the 3 previewNormals of this triangle
                gp_Dir NV1(Normals(N1)), NV2(Normals(N2)), NV3(Normals(N3));

                // transform the vertices and previewNormals to the place of the face
                if(!identity) {
                    V1.Transform(myTransf);
                    V2.Transform(myTransf);
                    V3.Transform(myTransf);
                    NV1.Transform(myTransf);
                    NV2.Transform(myTransf);
                    NV3.Transform(myTransf);
                }

                // add the previewNormals for all points of this triangle
                previewNorms[faceNodeOffset+N1-1] += SbVec3f(NV1.X(),NV1.Y(),NV1.Z());
                previewNorms[faceNodeOffset+N2-1] += SbVec3f(NV2.X(),NV2.Y(),NV2.Z());
                previewNorms[faceNodeOffset+N3-1] += SbVec3f(NV3.X(),NV3.Y(),NV3.Z());

                // set the vertices
                verts[faceNodeOffset+N1-1].setValue((float)(V1.X()),(float)(V1.Y()),(float)(V1.Z()));
                verts[faceNodeOffset+N2-1].setValue((float)(V2.X()),(float)(V2.Y()),(float)(V2.Z()));
                verts[faceNodeOffset+N3-1].setValue((float)(V3.X()),(float)(V3.Y()),(float)(V3.Z()));

                // set the index vector with the 3 point indexes and the end delimiter
                index[faceTriaOffset*4+4*(g-1)]   = faceNodeOffset+N1-1;
                index[faceTriaOffset*4+4*(g-1)+1] = faceNodeOffset+N2-1;
                index[faceTriaOffset*4+4*(g-1)+2] = faceNodeOffset+N3-1;
                index[faceTriaOffset*4+4*(g-1)+3] = SO_END_FACE_INDEX;
            }

            parts[ii] = nbTriInFace; // new part

            // counting up the per Face offsets
            faceNodeOffset += nbNodesInFace;
            faceTriaOffset += nbTriInFace;
        }

        // previewNormalize all previewNormals
        for (int i = 0; i< numNorms ;i++)
            previewNorms[i].normalize();

        // end the editing of the nodes
        previewCoords  ->point       .finishEditing();
        previewNorm    ->vector      .finishEditing();
        previewFaceSet ->coordIndex  .finishEditing();
        previewFaceSet ->partIndex   .finishEditing();
    }
    catch (...) {
        Base::Console().Error("Cannot compute Inventor representation for the shape of %s.\n",pcObject->getNameInDocument());
    }
}

void ViewProviderAddSub::updateData(const App::Property* p) {

    if(strcmp(p->getName(), "AddSubShape")==0)
        updateAddSubShapeIndicator();

    PartDesignGui::ViewProvider::updateData(p);
}

void ViewProviderAddSub::setPreviewDisplayMode(bool onoff) {

    if(onoff && displayMode!="Shape preview") {

        displayMode = getActiveDisplayMode();
        setDisplayMaskMode("Shape preview");
    }

    if(!onoff) {
        setDisplayMaskMode(displayMode.c_str());
    }

    App::DocumentObject* obj = static_cast<PartDesign::Feature*>(getObject())->BaseFeature.getValue();
    if(obj)
        static_cast<PartDesignGui::ViewProvider*>(Gui::Application::Instance->getViewProvider(obj))->makeTemporaryVisible(onoff);
}
<commit_msg>revert commit 1255ac62c and fix original code<commit_after>/***************************************************************************
 *   Copyright (c) 2015 Stefan Tröger <stefantroeger@gmx.net>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   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 Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"

#ifndef _PreComp_
#endif

#include "ViewProviderAddSub.h"
#include "Mod/Part/Gui/SoBrepFaceSet.h"
#include <Mod/PartDesign/App/FeatureAddSub.h>
#include <Gui/TaskView/TaskDialog.h>
#include <Gui/Control.h>
#include <Gui/Command.h>
#include <Gui/Application.h>
#include <Gui/BitmapFactory.h>
#include <Gui/Document.h>
#include <Base/Console.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoCoordinate3.h>
#include <Inventor/nodes/SoNormal.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoPickStyle.h>
#include <Bnd_Box.hxx>
#include <BRepBndLib.hxx>
#include <BRepMesh_IncrementalMesh.hxx>
#include <BRep_Tool.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <Standard_Version.hxx>

using namespace PartDesignGui;

PROPERTY_SOURCE(PartDesignGui::ViewProviderAddSub,PartDesignGui::ViewProvider)

ViewProviderAddSub::ViewProviderAddSub()
{

    previewShape = new SoSeparator();
    previewShape->ref();
    previewFaceSet = new PartGui::SoBrepFaceSet();
    previewFaceSet->ref();
    previewCoords = new SoCoordinate3();
    previewCoords->ref();
    previewNorm = new SoNormal();
    previewNorm->ref();

}

ViewProviderAddSub::~ViewProviderAddSub()
{
    previewFaceSet->unref();
    previewCoords->unref();
    previewNorm->unref();
    previewShape->unref();
}

void ViewProviderAddSub::attach(App::DocumentObject* obj) {

    ViewProvider::attach(obj);

    auto* bind = new SoMaterialBinding();
    bind->value = SoMaterialBinding::OVERALL;
    auto* material = new SoMaterial();
    if(static_cast<PartDesign::FeatureAddSub*>(obj)->getAddSubType() == PartDesign::FeatureAddSub::Additive)
        material->diffuseColor = SbColor(1,1,0);
    else
        material->diffuseColor = SbColor(1,0,0);

    material->transparency = 0.7f;
    auto* pick = new SoPickStyle();
    pick->style = SoPickStyle::UNPICKABLE;

    previewShape->addChild(pick);
    previewShape->addChild(bind);
    previewShape->addChild(material);
    previewShape->addChild(previewCoords);
    previewShape->addChild(previewNorm);
    previewShape->addChild(previewFaceSet);

    addDisplayMaskMode(previewShape, "Shape preview");
    updateAddSubShapeIndicator();
}

void ViewProviderAddSub::updateAddSubShapeIndicator() {

    TopoDS_Shape cShape(static_cast<PartDesign::FeatureAddSub*>(getObject())->AddSubShape.getValue());
    if (cShape.IsNull()) {
        previewCoords  ->point      .setNum(0);
        previewNorm    ->vector     .setNum(0);
        previewFaceSet ->coordIndex .setNum(0);
        previewFaceSet ->partIndex  .setNum(0);
        return;
    }

    int numTriangles=0,numNodes=0,numNorms=0,numFaces=0;
    std::set<int> faceEdges;

    try {
        // calculating the deflection value
        Bnd_Box bounds;
        BRepBndLib::Add(cShape, bounds);
        bounds.SetGap(0.0);
        Standard_Real xMin, yMin, zMin, xMax, yMax, zMax;
        bounds.Get(xMin, yMin, zMin, xMax, yMax, zMax);
        Standard_Real deflection = ((xMax-xMin)+(yMax-yMin)+(zMax-zMin))/300.0 *
            Deviation.getValue();

        // create or use the mesh on the data structure
#if OCC_VERSION_HEX >= 0x060600
        Standard_Real AngDeflectionRads = AngularDeflection.getValue() / 180.0 * M_PI;
        BRepMesh_IncrementalMesh(cShape,deflection,Standard_False,
                                    AngDeflectionRads,Standard_True);
#else
        BRepMesh_IncrementalMesh(cShape,deflection);
#endif
        // We must reset the location here because the transformation data
        // are set in the placement property
        TopLoc_Location aLoc;
        cShape.Location(aLoc);

        // count triangles and nodes in the mesh
        TopExp_Explorer Ex;
        for (Ex.Init(cShape,TopAbs_FACE);Ex.More();Ex.Next()) {
            Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(TopoDS::Face(Ex.Current()), aLoc);
            // Note: we must also count empty faces
            if (!mesh.IsNull()) {
                numTriangles += mesh->NbTriangles();
                numNodes     += mesh->NbNodes();
                numNorms     += mesh->NbNodes();
            }
            numFaces++;
        }

        // create memory for the nodes and indexes
        previewCoords  ->point      .setNum(numNodes);
        previewNorm    ->vector     .setNum(numNorms);
        previewFaceSet ->coordIndex .setNum(numTriangles*4);
        previewFaceSet ->partIndex  .setNum(numFaces);
        // get the raw memory for fast fill up
        SbVec3f* verts = previewCoords  ->point       .startEditing();
        SbVec3f* previewNorms = previewNorm    ->vector      .startEditing();
        int32_t* index = previewFaceSet ->coordIndex  .startEditing();
        int32_t* parts = previewFaceSet ->partIndex   .startEditing();

        // preset the previewNormal vector with null vector
        for (int i=0;i < numNorms;i++)
            previewNorms[i]= SbVec3f(0.0,0.0,0.0);

        int ii = 0,faceNodeOffset=0,faceTriaOffset=0;
        for (Ex.Init(cShape, TopAbs_FACE); Ex.More(); Ex.Next(),ii++) {
            TopLoc_Location aLoc;
            const TopoDS_Face &actFace = TopoDS::Face(Ex.Current());
            // get the mesh of the shape
            Handle (Poly_Triangulation) mesh = BRep_Tool::Triangulation(actFace,aLoc);
            if (mesh.IsNull()) continue;

            // getting the transformation of the shape/face
            gp_Trsf myTransf;
            Standard_Boolean identity = true;
            if (!aLoc.IsIdentity()) {
                identity = false;
                myTransf = aLoc.Transformation();
            }

            // getting size of node and triangle array of this face
            int nbNodesInFace = mesh->NbNodes();
            int nbTriInFace   = mesh->NbTriangles();
            // check orientation
            TopAbs_Orientation orient = actFace.Orientation();

            // cycling through the poly mesh
            const Poly_Array1OfTriangle& Triangles = mesh->Triangles();
            const TColgp_Array1OfPnt& Nodes = mesh->Nodes();
            TColgp_Array1OfDir Normals (Nodes.Lower(), Nodes.Upper());
            GetNormals(actFace, mesh, Normals);

            for (int g=1;g<=nbTriInFace;g++) {
                // Get the triangle
                Standard_Integer N1,N2,N3;
                Triangles(g).Get(N1,N2,N3);

                // change orientation of the triangle if the face is reversed
                if ( orient != TopAbs_FORWARD ) {
                    Standard_Integer tmp = N1;
                    N1 = N2;
                    N2 = tmp;
                }

                // get the 3 points of this triangle
                gp_Pnt V1(Nodes(N1)), V2(Nodes(N2)), V3(Nodes(N3));

                // get the 3 previewNormals of this triangle
                gp_Dir NV1(Normals(N1)), NV2(Normals(N2)), NV3(Normals(N3));

                // transform the vertices and previewNormals to the place of the face
                if(!identity) {
                    V1.Transform(myTransf);
                    V2.Transform(myTransf);
                    V3.Transform(myTransf);
                    NV1.Transform(myTransf);
                    NV2.Transform(myTransf);
                    NV3.Transform(myTransf);
                }

                // add the previewNormals for all points of this triangle
                previewNorms[faceNodeOffset+N1-1] += SbVec3f(NV1.X(),NV1.Y(),NV1.Z());
                previewNorms[faceNodeOffset+N2-1] += SbVec3f(NV2.X(),NV2.Y(),NV2.Z());
                previewNorms[faceNodeOffset+N3-1] += SbVec3f(NV3.X(),NV3.Y(),NV3.Z());

                // set the vertices
                verts[faceNodeOffset+N1-1].setValue((float)(V1.X()),(float)(V1.Y()),(float)(V1.Z()));
                verts[faceNodeOffset+N2-1].setValue((float)(V2.X()),(float)(V2.Y()),(float)(V2.Z()));
                verts[faceNodeOffset+N3-1].setValue((float)(V3.X()),(float)(V3.Y()),(float)(V3.Z()));

                // set the index vector with the 3 point indexes and the end delimiter
                index[faceTriaOffset*4+4*(g-1)]   = faceNodeOffset+N1-1;
                index[faceTriaOffset*4+4*(g-1)+1] = faceNodeOffset+N2-1;
                index[faceTriaOffset*4+4*(g-1)+2] = faceNodeOffset+N3-1;
                index[faceTriaOffset*4+4*(g-1)+3] = SO_END_FACE_INDEX;
            }

            parts[ii] = nbTriInFace; // new part

            // counting up the per Face offsets
            faceNodeOffset += nbNodesInFace;
            faceTriaOffset += nbTriInFace;
        }

        // previewNormalize all previewNormals
        for (int i = 0; i< numNorms ;i++)
            previewNorms[i].normalize();

        // end the editing of the nodes
        previewCoords  ->point       .finishEditing();
        previewNorm    ->vector      .finishEditing();
        previewFaceSet ->coordIndex  .finishEditing();
        previewFaceSet ->partIndex   .finishEditing();
    }
    catch (...) {
        Base::Console().Error("Cannot compute Inventor representation for the shape of %s.\n",pcObject->getNameInDocument());
    }
}

void ViewProviderAddSub::updateData(const App::Property* p) {

    if(strcmp(p->getName(), "AddSubShape")==0)
        updateAddSubShapeIndicator();

    PartDesignGui::ViewProvider::updateData(p);
}

void ViewProviderAddSub::setPreviewDisplayMode(bool onoff) {

    if(onoff && displayMode!="Shape preview") {

        displayMode = getActiveDisplayMode();
        setDisplayMaskMode("Shape preview");
    }

    if(!onoff) {
        setDisplayMaskMode(displayMode.c_str());
    }

    App::DocumentObject* obj = static_cast<PartDesign::Feature*>(getObject())->BaseFeature.getValue();
    if(obj)
        static_cast<PartDesignGui::ViewProvider*>(Gui::Application::Instance->getViewProvider(obj))->makeTemporaryVisible(onoff);
}
<|endoftext|>