<commit_before>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// EXTERNAL INCLUDES
#include <math.h>

// INTERNAL INCLUDES
#include "../shared/view.h"

#include <dali/dali.h>
#include <dali-toolkit/dali-toolkit.h>

using namespace Dali;

// LOCAL STUFF
namespace
{

const char * const TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
const char * const APPLICATION_TITLE_HIGHP( "Dissolve Effect(highp)" );
const char * const APPLICATION_TITLE_MEDIUMP( "Dissolve Effect(mediump)" );
const char * const EFFECT_HIGHP_IMAGE( DALI_IMAGE_DIR "icon-highp.png" );
const char * const EFFECT_MEDIUMP_IMAGE( DALI_IMAGE_DIR "icon-mediump.png" );
const char * const PLAY_ICON( DALI_IMAGE_DIR "icon-play.png" );
const char * const STOP_ICON( DALI_IMAGE_DIR "icon-stop.png" );

const char* IMAGES[] =
{
  DALI_IMAGE_DIR "gallery-large-1.jpg",
  DALI_IMAGE_DIR "gallery-large-2.jpg",
  DALI_IMAGE_DIR "gallery-large-3.jpg",
  DALI_IMAGE_DIR "gallery-large-4.jpg",
  DALI_IMAGE_DIR "gallery-large-5.jpg",
  DALI_IMAGE_DIR "gallery-large-6.jpg",
  DALI_IMAGE_DIR "gallery-large-7.jpg",
  DALI_IMAGE_DIR "gallery-large-8.jpg",
  DALI_IMAGE_DIR "gallery-large-9.jpg",
  DALI_IMAGE_DIR "gallery-large-10.jpg",
  DALI_IMAGE_DIR "gallery-large-11.jpg",
  DALI_IMAGE_DIR "gallery-large-12.jpg",
  DALI_IMAGE_DIR "gallery-large-13.jpg",
  DALI_IMAGE_DIR "gallery-large-14.jpg",
  DALI_IMAGE_DIR "gallery-large-15.jpg",
  DALI_IMAGE_DIR "gallery-large-16.jpg",
  DALI_IMAGE_DIR "gallery-large-17.jpg",
  DALI_IMAGE_DIR "gallery-large-18.jpg",
  DALI_IMAGE_DIR "gallery-large-19.jpg",
  DALI_IMAGE_DIR "gallery-large-20.jpg",
  DALI_IMAGE_DIR "gallery-large-21.jpg",
};

const int NUM_IMAGES( sizeof(IMAGES) / sizeof(IMAGES[0]) );

// The duration of the current image staying on screen when slideshow is on
const int VIEWINGTIME = 2000; // 2 seconds

const float TRANSITION_DURATION = 2.5f; //2.5 second

const float INITIAL_DEPTH = -10.0f;
} // namespace

class DissolveEffectApp : public ConnectionTracker
{
public:

  /**
   * Constructor
   * @param application class, stored as reference
   */
  DissolveEffectApp( Application& application );

  ~DissolveEffectApp();

private:

  /**
   * This method gets called once the main loop of application is up and running
   */
  void OnInit( Application& application );
  /**
   * PanGesture callback. This method gets called when the pan gesture is detected.
   * @param[in] actor The actor receiving the pan gesture.
   * @param[in] gesture The detected pan gesture.
   */
  void OnPanGesture( Actor actor, const PanGesture& gesture );

  /**
   * Set up the animations for transition
   * @param[in] position The point ( locates within rectange {(0,0),(0,1),(1,0),(1,1)} ) passing through the central line of the dissolve effect
   * @param[in] displacement The direction of the central line of the dissolve effect
   */
  void StartTransition(Vector2 position, Vector2 displacement);
  /**
   * Callback function of effect-switch button
   * Change the precision of the effect shader when the effect button is clicked
   * @param[in] button The handle of the clicked button
   */
  bool OnEffectButtonClicked( Toolkit::Button button );
  /**
   * Callback function of slideshow button
   * Start or stop the automatical image display when the slideshow button is clicked
   * @param[in] button The handle of the clicked button
   */
  bool OnSildeshowButtonClicked( Toolkit::Button button );
  /**
   * Callback function of cube transition completed signal
   * @param[in] effect The cube effect used for the transition
   * @param[in] imageActor The target imageActor of the completed transition
   */
  void OnTransitionCompleted(Animation& source);
  /**
   * Callback function of timer tick
   * The timer is used to count the image display duration after cube transition in slideshow,
   */
  bool OnTimerTick();

  /**
   * Main key event handler
   */
  void OnKeyEvent(const KeyEvent& event);

private:
  Application&                    mApplication;
  Toolkit::View                   mView;
  Toolkit::ToolBar                mToolBar;
  Layer                           mContent;
  Toolkit::TextView               mTitleActor;

  ImageActor                      mCurrentImage;
  ImageActor                      mNextImage;
  unsigned int                    mIndex;
  Constraint                      mSizeConstraint;

  Toolkit::DissolveEffect         mCurrentImageEffect;
  Toolkit::DissolveEffect         mNextImageEffect;
  bool                            mUseHighPrecision;
  Animation                       mAnimation;

  PanGestureDetector              mPanGestureDetector;
  bool                            mIsTransiting;

  bool                            mSlideshow;
  Timer                           mViewTimer;
  bool                            mTimerReady;
  unsigned int                    mCentralLineIndex;

  Image                           mIconPlay;
  Image                           mIconStop;
  Toolkit::PushButton             mPlayStopButton;

  Image                           mIconHighP;
  Image                           mIconMediumP;
  Toolkit::PushButton             mEffectChangeButton;
};

DissolveEffectApp::DissolveEffectApp( Application& application )
: mApplication( application ),
  mIndex( 0 ),
  mUseHighPrecision(true),
  mIsTransiting( false ),
  mSlideshow( false ),
  mTimerReady( false ),
  mCentralLineIndex( 0 )
{
  mApplication.InitSignal().Connect( this, &DissolveEffectApp::OnInit );
}

DissolveEffectApp::~DissolveEffectApp()
{
  //Nothing to do
}

void DissolveEffectApp::OnInit( Application& application )
{
  Stage::GetCurrent().KeyEventSignal().Connect(this, &DissolveEffectApp::OnKeyEvent);

  // Creates a default view with a default tool bar, the view is added to the stage.
  mContent = DemoHelper::CreateView( application, mView,mToolBar, "", TOOLBAR_IMAGE, "" );

  // Add an effect-changing button on the right of the tool bar.
  mIconHighP = ResourceImage::New( EFFECT_HIGHP_IMAGE );
  mIconMediumP = ResourceImage::New( EFFECT_MEDIUMP_IMAGE );
  mEffectChangeButton = Toolkit::PushButton::New();
  mEffectChangeButton.SetBackgroundImage(mIconHighP);
  mEffectChangeButton.ClickedSignal().Connect( this, &DissolveEffectApp::OnEffectButtonClicked );
  mToolBar.AddControl( mEffectChangeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING );

  // Add title to the tool bar.
  mTitleActor = Toolkit::TextView::New();
  mTitleActor.SetText( APPLICATION_TITLE_HIGHP );
  mTitleActor.SetStyleToCurrentText(DemoHelper::GetDefaultTextStyle());
  mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Toolkit::Alignment::HorizontalCenter );

  // Add an slide-show button on the right of the title
  mIconPlay = ResourceImage::New( PLAY_ICON );
  mIconStop = ResourceImage::New( STOP_ICON );
  mPlayStopButton = Toolkit::PushButton::New();
  mPlayStopButton.SetBackgroundImage( mIconPlay );
  mPlayStopButton.ClickedSignal().Connect( this, &DissolveEffectApp::OnSildeshowButtonClicked );
  mToolBar.AddControl( mPlayStopButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalCenter, DemoHelper::DEFAULT_PLAY_PADDING );

  // use pan gesture to detect the cursor or finger movement
  mPanGestureDetector = PanGestureDetector::New();
  mPanGestureDetector.DetectedSignal().Connect( this, &DissolveEffectApp::OnPanGesture );

  // create the dissolve effect object
  mCurrentImageEffect = Toolkit::DissolveEffect::New(mUseHighPrecision);
  mNextImageEffect = Toolkit::DissolveEffect::New(mUseHighPrecision);

  mViewTimer = Timer::New( VIEWINGTIME );
  mViewTimer.TickSignal().Connect( this, &DissolveEffectApp::OnTimerTick );
  mTimerReady = true;

  mSizeConstraint= Constraint::New<Vector3>( Actor::SCALE, LocalSource( Actor::SIZE ), ParentSource( Actor::SIZE ), ScaleToFitKeepAspectRatioConstraint() );

  // show the first image
  mCurrentImage = ImageActor::New( ResourceImage::New( IMAGES[mIndex] ) );
  mCurrentImage.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
  mCurrentImage.ApplyConstraint( mSizeConstraint );
  mContent.Add(mCurrentImage);
  mPanGestureDetector.Attach( mCurrentImage );
}

// signal handler, called when the pan gesture is detected
void DissolveEffectApp::OnPanGesture( Actor actor, const PanGesture& gesture )
{
  // does not response when the animation has not finished
  if( mIsTransiting || mSlideshow )
  {
    return;
  }

  if( gesture.state == Gesture::Continuing )
  {
    if( gesture.displacement.x < 0)
    {
      mIndex = (mIndex + 1)%NUM_IMAGES;
    }
    else
    {
      mIndex = (mIndex + NUM_IMAGES -1)%NUM_IMAGES;
    }

    Image image = ResourceImage::New( IMAGES[ mIndex ] );
    mNextImage = ImageActor::New( image );
    mNextImage.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
    mNextImage.ApplyConstraint( mSizeConstraint );
    mNextImage.SetZ(INITIAL_DEPTH);
    mContent.Add(mNextImage);
    Vector2 size = Vector2( mCurrentImage.GetCurrentSize() );
    StartTransition( gesture.position / size, gesture.displacement * Vector2(1.0, size.x/size.y));
  }
}

void DissolveEffectApp::StartTransition(Vector2 position, Vector2 displacement)
{
  mAnimation = Animation::New(TRANSITION_DURATION);

  mCurrentImageEffect.SetCentralLine(position,displacement);
  mCurrentImageEffect.SetDistortion(0.0f);
  mCurrentImage.SetShaderEffect(mCurrentImageEffect);
  mAnimation.AnimateTo( Property(mCurrentImageEffect, mCurrentImageEffect.GetDistortionPropertyName()), 1.0f, AlphaFunctions::Linear );

  mNextImage.SetOpacity(0.0f);
  mAnimation.OpacityTo( mNextImage, 1.0, AlphaFunctions::Linear );

  if(mUseHighPrecision)
  {
    mNextImageEffect.SetCentralLine(position,-displacement);
    mNextImageEffect.SetDistortion(1.0f);
    mNextImage.SetShaderEffect(mNextImageEffect);
    mAnimation.AnimateTo( Property(mNextImageEffect, mNextImageEffect.GetDistortionPropertyName()), 0.0f, AlphaFunctions::Linear );
  }
  else
  {
    mAnimation.MoveTo(mNextImage, Vector3(0.0f, 0.0f, 0.0f), AlphaFunctions::Linear);
  }

  mAnimation.FinishedSignal().Connect( this, &DissolveEffectApp::OnTransitionCompleted );
  mAnimation.Play();
  mIsTransiting = true;
}

void DissolveEffectApp::OnKeyEvent(const KeyEvent& event)
{
  if(event.state == KeyEvent::Down)
  {
    if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
    {
      mApplication.Quit();
    }
  }
}

bool DissolveEffectApp::OnEffectButtonClicked( Toolkit::Button button )
{
  mUseHighPrecision = !mUseHighPrecision;
  mCurrentImageEffect = Toolkit::DissolveEffect::New(mUseHighPrecision);
  if(mUseHighPrecision)
  {
    mTitleActor.SetText( APPLICATION_TITLE_HIGHP );
    mEffectChangeButton.SetBackgroundImage(mIconHighP);
  }
  else
  {
    mTitleActor.SetText( APPLICATION_TITLE_MEDIUMP );
    mEffectChangeButton.SetBackgroundImage(mIconMediumP);
  }
  mTitleActor.SetStyleToCurrentText(DemoHelper::GetDefaultTextStyle());

  return true;
}

bool DissolveEffectApp::OnSildeshowButtonClicked( Toolkit::Button button )
{
  mSlideshow = !mSlideshow;
  if( mSlideshow )
  {
    mPlayStopButton.SetBackgroundImage( mIconStop );
    mPanGestureDetector.Detach( mContent );
    mViewTimer.Start();
    mTimerReady = false;
  }
  else
  {
    mPlayStopButton.SetBackgroundImage( mIconPlay );
    mTimerReady = true;
    mPanGestureDetector.Attach( mContent );
  }
  return true;
}

void DissolveEffectApp::OnTransitionCompleted( Animation& source )
{
  mCurrentImage.RemoveShaderEffect();
  mNextImage.RemoveShaderEffect();
  mContent.Remove(mCurrentImage);
  mPanGestureDetector.Detach( mCurrentImage );
  mCurrentImage = mNextImage;
  mPanGestureDetector.Attach( mCurrentImage );
  mIsTransiting = false;

  if( mSlideshow)
  {
    mViewTimer.Start();
    mTimerReady = false;
  }
}

bool DissolveEffectApp::OnTimerTick()
{
  mTimerReady = true;
  if(mSlideshow)
  {
    mIndex = (mIndex + 1)%NUM_IMAGES;
    Image image = ResourceImage::New( IMAGES[ mIndex ] );
    mNextImage = ImageActor::New( image );
    mNextImage.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
    mNextImage.ApplyConstraint( mSizeConstraint );
    mNextImage.SetZ(INITIAL_DEPTH);
    mContent.Add(mNextImage);
    switch(mCentralLineIndex%4)
    {
      case 0:
      {
        StartTransition(Vector2(1.0f,0.5f), Vector2(-1.0f, 0.0f));
        break;
      }
      case 1:
      {
        StartTransition(Vector2(0.5f,0.0f), Vector2(0.0f, 1.0f));
        break;
      }
      case 2:
      {
        StartTransition(Vector2(0.0f,0.5f), Vector2(1.0f, 0.0f));
        break;
      }
      default:
      {
        StartTransition(Vector2(0.5f,1.0f), Vector2(0.0f, -1.0f));
        break;
      }

    }
    mCentralLineIndex++;
  }
  return false;   //return false to stop the timer
}

// Entry point for Linux & SLP applications
int main( int argc, char **argv )
{
  Application application = Application::New( &argc, &argv );
  DissolveEffectApp test( application );
  application.MainLoop();

  return 0;
}
<commit_msg>Fixed dissolve effect first image appearing smaller than viewport<commit_after>/*
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// EXTERNAL INCLUDES
#include <math.h>

// INTERNAL INCLUDES
#include "../shared/view.h"

#include <dali/dali.h>
#include <dali-toolkit/dali-toolkit.h>

using namespace Dali;

// LOCAL STUFF
namespace
{

const char * const TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
const char * const APPLICATION_TITLE_HIGHP( "Dissolve Effect(highp)" );
const char * const APPLICATION_TITLE_MEDIUMP( "Dissolve Effect(mediump)" );
const char * const EFFECT_HIGHP_IMAGE( DALI_IMAGE_DIR "icon-highp.png" );
const char * const EFFECT_MEDIUMP_IMAGE( DALI_IMAGE_DIR "icon-mediump.png" );
const char * const PLAY_ICON( DALI_IMAGE_DIR "icon-play.png" );
const char * const STOP_ICON( DALI_IMAGE_DIR "icon-stop.png" );

const char* IMAGES[] =
{
  DALI_IMAGE_DIR "gallery-large-1.jpg",
  DALI_IMAGE_DIR "gallery-large-2.jpg",
  DALI_IMAGE_DIR "gallery-large-3.jpg",
  DALI_IMAGE_DIR "gallery-large-4.jpg",
  DALI_IMAGE_DIR "gallery-large-5.jpg",
  DALI_IMAGE_DIR "gallery-large-6.jpg",
  DALI_IMAGE_DIR "gallery-large-7.jpg",
  DALI_IMAGE_DIR "gallery-large-8.jpg",
  DALI_IMAGE_DIR "gallery-large-9.jpg",
  DALI_IMAGE_DIR "gallery-large-10.jpg",
  DALI_IMAGE_DIR "gallery-large-11.jpg",
  DALI_IMAGE_DIR "gallery-large-12.jpg",
  DALI_IMAGE_DIR "gallery-large-13.jpg",
  DALI_IMAGE_DIR "gallery-large-14.jpg",
  DALI_IMAGE_DIR "gallery-large-15.jpg",
  DALI_IMAGE_DIR "gallery-large-16.jpg",
  DALI_IMAGE_DIR "gallery-large-17.jpg",
  DALI_IMAGE_DIR "gallery-large-18.jpg",
  DALI_IMAGE_DIR "gallery-large-19.jpg",
  DALI_IMAGE_DIR "gallery-large-20.jpg",
  DALI_IMAGE_DIR "gallery-large-21.jpg",
};

const int NUM_IMAGES( sizeof(IMAGES) / sizeof(IMAGES[0]) );

// The duration of the current image staying on screen when slideshow is on
const int VIEWINGTIME = 2000; // 2 seconds

const float TRANSITION_DURATION = 2.5f; //2.5 second

const float INITIAL_DEPTH = -10.0f;
} // namespace

class DissolveEffectApp : public ConnectionTracker
{
public:

  /**
   * Constructor
   * @param application class, stored as reference
   */
  DissolveEffectApp( Application& application );

  ~DissolveEffectApp();

private:

  /**
   * This method gets called once the main loop of application is up and running
   */
  void OnInit( Application& application );
  /**
   * PanGesture callback. This method gets called when the pan gesture is detected.
   * @param[in] actor The actor receiving the pan gesture.
   * @param[in] gesture The detected pan gesture.
   */
  void OnPanGesture( Actor actor, const PanGesture& gesture );

  /**
   * Set up the animations for transition
   * @param[in] position The point ( locates within rectange {(0,0),(0,1),(1,0),(1,1)} ) passing through the central line of the dissolve effect
   * @param[in] displacement The direction of the central line of the dissolve effect
   */
  void StartTransition(Vector2 position, Vector2 displacement);
  /**
   * Callback function of effect-switch button
   * Change the precision of the effect shader when the effect button is clicked
   * @param[in] button The handle of the clicked button
   */
  bool OnEffectButtonClicked( Toolkit::Button button );
  /**
   * Callback function of slideshow button
   * Start or stop the automatical image display when the slideshow button is clicked
   * @param[in] button The handle of the clicked button
   */
  bool OnSildeshowButtonClicked( Toolkit::Button button );
  /**
   * Callback function of cube transition completed signal
   * @param[in] effect The cube effect used for the transition
   * @param[in] imageActor The target imageActor of the completed transition
   */
  void OnTransitionCompleted(Animation& source);
  /**
   * Callback function of timer tick
   * The timer is used to count the image display duration after cube transition in slideshow,
   */
  bool OnTimerTick();

  /**
   * Main key event handler
   */
  void OnKeyEvent(const KeyEvent& event);

private:
  Application&                    mApplication;
  Toolkit::View                   mView;
  Toolkit::ToolBar                mToolBar;
  Layer                           mContent;
  Toolkit::TextView               mTitleActor;
  Actor                           mParent;

  ImageActor                      mCurrentImage;
  ImageActor                      mNextImage;
  unsigned int                    mIndex;
  Constraint                      mSizeConstraint;

  Toolkit::DissolveEffect         mCurrentImageEffect;
  Toolkit::DissolveEffect         mNextImageEffect;
  bool                            mUseHighPrecision;
  Animation                       mAnimation;

  PanGestureDetector              mPanGestureDetector;
  bool                            mIsTransiting;

  bool                            mSlideshow;
  Timer                           mViewTimer;
  bool                            mTimerReady;
  unsigned int                    mCentralLineIndex;

  Image                           mIconPlay;
  Image                           mIconStop;
  Toolkit::PushButton             mPlayStopButton;

  Image                           mIconHighP;
  Image                           mIconMediumP;
  Toolkit::PushButton             mEffectChangeButton;
};

DissolveEffectApp::DissolveEffectApp( Application& application )
: mApplication( application ),
  mIndex( 0 ),
  mUseHighPrecision(true),
  mIsTransiting( false ),
  mSlideshow( false ),
  mTimerReady( false ),
  mCentralLineIndex( 0 )
{
  mApplication.InitSignal().Connect( this, &DissolveEffectApp::OnInit );
}

DissolveEffectApp::~DissolveEffectApp()
{
  //Nothing to do
}

void DissolveEffectApp::OnInit( Application& application )
{
  Stage::GetCurrent().KeyEventSignal().Connect(this, &DissolveEffectApp::OnKeyEvent);

  // Creates a default view with a default tool bar, the view is added to the stage.
  mContent = DemoHelper::CreateView( application, mView,mToolBar, "", TOOLBAR_IMAGE, "" );

  // Add an effect-changing button on the right of the tool bar.
  mIconHighP = ResourceImage::New( EFFECT_HIGHP_IMAGE );
  mIconMediumP = ResourceImage::New( EFFECT_MEDIUMP_IMAGE );
  mEffectChangeButton = Toolkit::PushButton::New();
  mEffectChangeButton.SetBackgroundImage(mIconHighP);
  mEffectChangeButton.ClickedSignal().Connect( this, &DissolveEffectApp::OnEffectButtonClicked );
  mToolBar.AddControl( mEffectChangeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING );

  // Add title to the tool bar.
  mTitleActor = Toolkit::TextView::New();
  mTitleActor.SetText( APPLICATION_TITLE_HIGHP );
  mTitleActor.SetStyleToCurrentText(DemoHelper::GetDefaultTextStyle());
  mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Toolkit::Alignment::HorizontalCenter );

  // Add an slide-show button on the right of the title
  mIconPlay = ResourceImage::New( PLAY_ICON );
  mIconStop = ResourceImage::New( STOP_ICON );
  mPlayStopButton = Toolkit::PushButton::New();
  mPlayStopButton.SetBackgroundImage( mIconPlay );
  mPlayStopButton.ClickedSignal().Connect( this, &DissolveEffectApp::OnSildeshowButtonClicked );
  mToolBar.AddControl( mPlayStopButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalCenter, DemoHelper::DEFAULT_PLAY_PADDING );

  // use pan gesture to detect the cursor or finger movement
  mPanGestureDetector = PanGestureDetector::New();
  mPanGestureDetector.DetectedSignal().Connect( this, &DissolveEffectApp::OnPanGesture );

  // create the dissolve effect object
  mCurrentImageEffect = Toolkit::DissolveEffect::New(mUseHighPrecision);
  mNextImageEffect = Toolkit::DissolveEffect::New(mUseHighPrecision);

  mViewTimer = Timer::New( VIEWINGTIME );
  mViewTimer.TickSignal().Connect( this, &DissolveEffectApp::OnTimerTick );
  mTimerReady = true;

  // Set size to stage size to avoid seeing a black border on transition
  mParent = Actor::New();
  mParent.SetSize( Stage::GetCurrent().GetSize() );
  mParent.SetPositionInheritanceMode( USE_PARENT_POSITION );
  mContent.Add( mParent );

  mSizeConstraint= Constraint::New<Vector3>( Actor::SCALE, LocalSource( Actor::SIZE ), ParentSource( Actor::SIZE ), ScaleToFitKeepAspectRatioConstraint() );

  // show the first image
  mCurrentImage = ImageActor::New( ResourceImage::New( IMAGES[mIndex] ) );
  mCurrentImage.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
  mCurrentImage.ApplyConstraint( mSizeConstraint );
  mParent.Add( mCurrentImage );

  mPanGestureDetector.Attach( mCurrentImage );
}

// signal handler, called when the pan gesture is detected
void DissolveEffectApp::OnPanGesture( Actor actor, const PanGesture& gesture )
{
  // does not response when the animation has not finished
  if( mIsTransiting || mSlideshow )
  {
    return;
  }

  if( gesture.state == Gesture::Continuing )
  {
    if( gesture.displacement.x < 0)
    {
      mIndex = (mIndex + 1)%NUM_IMAGES;
    }
    else
    {
      mIndex = (mIndex + NUM_IMAGES -1)%NUM_IMAGES;
    }

    Image image = ResourceImage::New( IMAGES[ mIndex ] );
    mNextImage = ImageActor::New( image );
    mNextImage.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
    mNextImage.ApplyConstraint( mSizeConstraint );
    mNextImage.SetZ(INITIAL_DEPTH);
    mParent.Add( mNextImage );
    Vector2 size = Vector2( mCurrentImage.GetCurrentSize() );
    StartTransition( gesture.position / size, gesture.displacement * Vector2(1.0, size.x/size.y));
  }
}

void DissolveEffectApp::StartTransition(Vector2 position, Vector2 displacement)
{
  mAnimation = Animation::New(TRANSITION_DURATION);

  mCurrentImageEffect.SetCentralLine(position,displacement);
  mCurrentImageEffect.SetDistortion(0.0f);
  mCurrentImage.SetShaderEffect(mCurrentImageEffect);
  mAnimation.AnimateTo( Property(mCurrentImageEffect, mCurrentImageEffect.GetDistortionPropertyName()), 1.0f, AlphaFunctions::Linear );

  mNextImage.SetOpacity(0.0f);
  mAnimation.OpacityTo( mNextImage, 1.0, AlphaFunctions::Linear );

  if(mUseHighPrecision)
  {
    mNextImageEffect.SetCentralLine(position,-displacement);
    mNextImageEffect.SetDistortion(1.0f);
    mNextImage.SetShaderEffect(mNextImageEffect);
    mAnimation.AnimateTo( Property(mNextImageEffect, mNextImageEffect.GetDistortionPropertyName()), 0.0f, AlphaFunctions::Linear );
  }
  else
  {
    mAnimation.MoveTo(mNextImage, Vector3(0.0f, 0.0f, 0.0f), AlphaFunctions::Linear);
  }

  mAnimation.FinishedSignal().Connect( this, &DissolveEffectApp::OnTransitionCompleted );
  mAnimation.Play();
  mIsTransiting = true;
}

void DissolveEffectApp::OnKeyEvent(const KeyEvent& event)
{
  if(event.state == KeyEvent::Down)
  {
    if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
    {
      mApplication.Quit();
    }
  }
}

bool DissolveEffectApp::OnEffectButtonClicked( Toolkit::Button button )
{
  mUseHighPrecision = !mUseHighPrecision;
  mCurrentImageEffect = Toolkit::DissolveEffect::New(mUseHighPrecision);
  if(mUseHighPrecision)
  {
    mTitleActor.SetText( APPLICATION_TITLE_HIGHP );
    mEffectChangeButton.SetBackgroundImage(mIconHighP);
  }
  else
  {
    mTitleActor.SetText( APPLICATION_TITLE_MEDIUMP );
    mEffectChangeButton.SetBackgroundImage(mIconMediumP);
  }
  mTitleActor.SetStyleToCurrentText(DemoHelper::GetDefaultTextStyle());

  return true;
}

bool DissolveEffectApp::OnSildeshowButtonClicked( Toolkit::Button button )
{
  mSlideshow = !mSlideshow;
  if( mSlideshow )
  {
    mPlayStopButton.SetBackgroundImage( mIconStop );
    mPanGestureDetector.Detach( mParent );
    mViewTimer.Start();
    mTimerReady = false;
  }
  else
  {
    mPlayStopButton.SetBackgroundImage( mIconPlay );
    mTimerReady = true;
    mPanGestureDetector.Attach( mParent );
  }
  return true;
}

void DissolveEffectApp::OnTransitionCompleted( Animation& source )
{
  mCurrentImage.RemoveShaderEffect();
  mNextImage.RemoveShaderEffect();
  mParent.Remove( mCurrentImage );
  mPanGestureDetector.Detach( mCurrentImage );
  mCurrentImage = mNextImage;
  mPanGestureDetector.Attach( mCurrentImage );
  mIsTransiting = false;

  if( mSlideshow)
  {
    mViewTimer.Start();
    mTimerReady = false;
  }
}

bool DissolveEffectApp::OnTimerTick()
{
  mTimerReady = true;
  if(mSlideshow)
  {
    mIndex = (mIndex + 1)%NUM_IMAGES;
    Image image = ResourceImage::New( IMAGES[ mIndex ] );
    mNextImage = ImageActor::New( image );
    mNextImage.SetPositionInheritanceMode(USE_PARENT_POSITION_PLUS_LOCAL_POSITION);
    mNextImage.ApplyConstraint( mSizeConstraint );
    mNextImage.SetZ(INITIAL_DEPTH);
    mParent.Add( mNextImage );
    switch( mCentralLineIndex%4 )
    {
      case 0:
      {
        StartTransition(Vector2(1.0f,0.5f), Vector2(-1.0f, 0.0f));
        break;
      }
      case 1:
      {
        StartTransition(Vector2(0.5f,0.0f), Vector2(0.0f, 1.0f));
        break;
      }
      case 2:
      {
        StartTransition(Vector2(0.0f,0.5f), Vector2(1.0f, 0.0f));
        break;
      }
      default:
      {
        StartTransition(Vector2(0.5f,1.0f), Vector2(0.0f, -1.0f));
        break;
      }

    }
    mCentralLineIndex++;
  }
  return false;   //return false to stop the timer
}

// Entry point for Linux & SLP applications
int main( int argc, char **argv )
{
  Application application = Application::New( &argc, &argv );
  DissolveEffectApp test( application );
  application.MainLoop();

  return 0;
}
<|endoftext|>


<commit_before>#include "Piezas.h"
#include <vector>
/** CLASS Piezas
 * Class for representing a Piezas vertical board, which is roughly based
 * on the game "Connect Four" where pieces are placed in a column and
 * fall to the bottom of the column, or on top of other pieces already in
 * that column. For an illustration of the board, see:
 *  https://en.wikipedia.org/wiki/Connect_Four
 *
 * Board coordinates [row,col] should match with:
 * [2,0][2,1][2,2][2,3]
 * [1,0][1,1][1,2][1,3]
 * [0,0][0,1][0,2][0,3]
 * So that a piece dropped in column 2 should take [0,2] and the next one
 * dropped in column 2 should take [1,2].
**/

/**
 * Constructor sets an empty board (default 3 rows, 4 columns) and
 * specifies it is X's turn first
**/
Piezas::Piezas();
{
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      board[i][j] = Blank;
    }
  }
  turn = X;
}

/**
 * Resets each board location to the Blank Piece value, with a board of the
 * same size as previously specified
**/
void Piezas::reset();
{
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      board[i][j] = Blank;
    }
  }
}

/**
 * Places a piece of the current turn on the board, returns what
 * piece is placed, and toggles which Piece's turn it is. dropPiece does
 * NOT allow to place a piece in a location where a column is full.
 * In that case, placePiece returns Piece Blank value
 * Out of bounds coordinates return the Piece Invalid value
 * Trying to drop a piece where it cannot be placed loses the player's turn
**/
Piece Piezas::dropPiece(int column);
{
  if (column >= BOARD_COLS || column < 0)
  {
    if (turn == X)
    {
      turn = O;
    }
    else
    {
      turn = X;
    }
    return Invalid;
  }
  if (board[2][column] != Blank)
  {
    if (turn == X)
    {
      turn = O;
    }
    else
    {
      turn = X;
    }
    return Blank;
  }
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    if (board[i][column] == Blank)
    {
      board[i][column] = turn;
      break;
    }
  }
  if (turn == X)
  {
    turn = O;
    return X;
  }
  else
  {
    turn = X;
    return O;
  }
}

/**
 * Returns what piece is at the provided coordinates, or Blank if there
 * are no pieces there, or Invalid if the coordinates are out of bounds
**/
Piece Piezas::pieceAt(int row, int column);
{
  if (row >= BOARD_ROWS || row < 0 || column >= BOARD_COLS || column < 0)
  {
    return Invalid;
  }
  return board[row][column];
}

/**
 * Returns which Piece has won, if there is a winner, Invalid if the game
 * is not over, or Blank if the board is filled and no one has won ("tie").
 * For a game to be over, all locations on the board must be filled with X's
 * and O's (i.e. no remaining Blank spaces). The winner is which player has
 * the most adjacent pieces in a single line. Lines can go either vertically
 * or horizontally. If both X's and O's have the same max number of pieces in a
 * line, it is a tie.
**/
Piece Piezas::gameState();
{

}
<commit_msg>Piezas v1, all functions programmed<commit_after>#include "Piezas.h"
#include <vector>
/** CLASS Piezas
 * Class for representing a Piezas vertical board, which is roughly based
 * on the game "Connect Four" where pieces are placed in a column and
 * fall to the bottom of the column, or on top of other pieces already in
 * that column. For an illustration of the board, see:
 *  https://en.wikipedia.org/wiki/Connect_Four
 *
 * Board coordinates [row,col] should match with:
 * [2,0][2,1][2,2][2,3]
 * [1,0][1,1][1,2][1,3]
 * [0,0][0,1][0,2][0,3]
 * So that a piece dropped in column 2 should take [0,2] and the next one
 * dropped in column 2 should take [1,2].
**/

/**
 * Constructor sets an empty board (default 3 rows, 4 columns) and
 * specifies it is X's turn first
**/
Piezas::Piezas();
{
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      board[i][j] = Blank;
    }
  }
  turn = X;
}

/**
 * Resets each board location to the Blank Piece value, with a board of the
 * same size as previously specified
**/
void Piezas::reset();
{
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      board[i][j] = Blank;
    }
  }
}

/**
 * Places a piece of the current turn on the board, returns what
 * piece is placed, and toggles which Piece's turn it is. dropPiece does
 * NOT allow to place a piece in a location where a column is full.
 * In that case, placePiece returns Piece Blank value
 * Out of bounds coordinates return the Piece Invalid value
 * Trying to drop a piece where it cannot be placed loses the player's turn
**/
Piece Piezas::dropPiece(int column);
{
  if (column >= BOARD_COLS || column < 0)
  {
    if (turn == X)
    {
      turn = O;
    }
    else
    {
      turn = X;
    }
    return Invalid;
  }
  if (board[2][column] != Blank)
  {
    if (turn == X)
    {
      turn = O;
    }
    else
    {
      turn = X;
    }
    return Blank;
  }
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    if (board[i][column] == Blank)
    {
      board[i][column] = turn;
      break;
    }
  }
  if (turn == X)
  {
    turn = O;
    return X;
  }
  else
  {
    turn = X;
    return O;
  }
}

/**
 * Returns what piece is at the provided coordinates, or Blank if there
 * are no pieces there, or Invalid if the coordinates are out of bounds
**/
Piece Piezas::pieceAt(int row, int column);
{
  if (row >= BOARD_ROWS || row < 0 || column >= BOARD_COLS || column < 0)
  {
    return Invalid;
  }
  return board[row][column];
}

/**
 * Returns which Piece has won, if there is a winner, Invalid if the game
 * is not over, or Blank if the board is filled and no one has won ("tie").
 * For a game to be over, all locations on the board must be filled with X's
 * and O's (i.e. no remaining Blank spaces). The winner is which player has
 * the most adjacent pieces in a single line. Lines can go either vertically
 * or horizontally. If both X's and O's have the same max number of pieces in a
 * line, it is a tie.
**/
Piece Piezas::gameState();
{
  for (int i = 0; i < BOARD_COLS; i++)
  {
    if (board[2][i] == Blank)
      return Invalid;
  }
  int xs = 0;
  int os = 0;
  piece prev = Invalid;
  int inarow = 0;
  for (int i = 0; i < BOARD_ROWS; i++)
  {
    for (int j = 0; j < BOARD_COLS; j++)
    {
      if (board[i][j] == prev)
      {
        inarow++;
        if (board[i][j] == X && xs < inarow)
        {
          xs = inarow;
        }
        if (board[i][j] == O && os < inarow)
        {
          os = inarow;
        }
      }
      else
      {
        prev = board[i][j];
        inarow = 0;
      }
    }
    prev = Invalid;
    inarow = 0;
  }
  for (int i = 0; i < BOARD_COLS; i++)
  {
    for (int j = 0; j < BOARD_ROWS; j++)
    {
      if (board[j][i] == prev)
      {
        inarow++;
        if (board[j][i] == X && xs < inarow)
        {
          xs = inarow;
        }
        if (board[j][i] == O && os < inarow)
        {
          os = inarow;
        }
      }
      else
      {
        prev = board[j][i];
        inarow = 0;
      }
    }
    prev = Invalid;
    inarow = 0;
  }
  if (xs == os)
  {
    return Blank;
  }
  if (xs > os)
  {
    return X;
  }
  if (os > xs)
  {
    return O;
  }
}
<|endoftext|>


<commit_before> #include <gtest/gtest.h>
#include <exception>
#include <algorithm>

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ;

#include <string>
using std::string ;

#include <sofa/helper/system/FileMonitor.h>
using sofa::helper::system::FileEventListener ;
using sofa::helper::system::FileMonitor ;

#ifdef WIN32
#include <windows.h>
#endif

static std::string getPath(std::string s) {
    return std::string(FRAMEWORK_TEST_RESOURCES_DIR) + std::string("/") + s;
}

void createAFilledFile(const string filename, unsigned int rep){
    ofstream file1 ;
    file1.open(filename.c_str(), ofstream::out) ;

    //throw_when(!file1.is_open()) ;

    string sample = "#include<TODOD> int main(int argc...){ ... }\n}" ;
    for(unsigned int i=0;i<rep;i++){
        file1.write(sample.c_str(), sample.size()) ;
    }
    file1.close();
}

void waitForFileEvents()
{
    // on osx there is a latency between 0.2 and 0.5s for file events...
#ifdef __APPLE__
	sleep(1);
#endif
	// on windows we use file date, which resoution is assumed (by us) to be below this value in ms
#ifdef WIN32
	Sleep(100);
#endif
}

class MyFileListener : public FileEventListener
{
public:
    vector<string> m_files ;

    virtual void fileHasChanged(const std::string& filename){
        //std::cout << "FileHasChanged: " << filename << std::endl ;
        m_files.push_back(filename) ;
    }
};

TEST(FileMonitor, addFileNotExist_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath("nonexisting.txt"), &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileNotExist2_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath(""),"nonexisting.txt", &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileExist_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileTwice_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener);

    // Retry to add an existing file. It should fail.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;

    waitForFileEvents();
    FileMonitor::updates(0) ;

    // The listener should be notified 1 times with the same event.
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, noUpdate_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, updateNoChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    waitForFileEvents();
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
	waitForFileEvents();
	FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    waitForFileEvents();
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChangeTwice_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 100) ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    waitForFileEvents();
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileListenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeFileListener(getPath("existing.txt"), &listener1) ;

    waitForFileEvents();
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, listenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    FileMonitor::updates(0) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeListener(&listener1) ;

    waitForFileEvents();
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, fileChange2_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath(""),"existing.txt", &listener) ;
    waitForFileEvents();
    FileMonitor::updates(0) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;

    waitForFileEvents();
    FileMonitor::updates(0) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}
<commit_msg>FileMonitor_test update: no more wait on Mac, and use of timeouts in tests.<commit_after> #include <gtest/gtest.h>
#include <exception>
#include <algorithm>

#include <vector>
using std::vector ;

#include <fstream>
using std::ofstream ;

#include <string>
using std::string ;

#include <sofa/helper/system/FileMonitor.h>
using sofa::helper::system::FileEventListener ;
using sofa::helper::system::FileMonitor ;

#ifdef WIN32
#include <windows.h>
#endif

static std::string getPath(std::string s) {
    return std::string(FRAMEWORK_TEST_RESOURCES_DIR) + std::string("/") + s;
}

void createAFilledFile(const string filename, unsigned int rep){
    ofstream file1 ;
    file1.open(filename.c_str(), ofstream::out) ;

    //throw_when(!file1.is_open()) ;

    string sample = "#include<TODOD> int main(int argc...){ ... }\n}" ;
    for(unsigned int i=0;i<rep;i++){
        file1.write(sample.c_str(), sample.size()) ;
    }
    file1.close();
}

void waitForFileEvents()
{
	// on windows we use file date, which resoution is assumed (by us) to be below this value in ms
#ifdef WIN32
	Sleep(100);
#endif
}

class MyFileListener : public FileEventListener
{
public:
    vector<string> m_files ;

    virtual void fileHasChanged(const std::string& filename){
        //std::cout << "FileHasChanged: " << filename << std::endl ;
        m_files.push_back(filename) ;
    }
};

TEST(FileMonitor, addFileNotExist_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath("nonexisting.txt"), &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileNotExist2_test)
{
    MyFileListener listener ;

    // Should refuse to add a file that does not exists
     EXPECT_EQ( FileMonitor::addFile(getPath(""),"nonexisting.txt", &listener), -1 ) ;

     FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileExist_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, addFileTwice_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener);

    // Retry to add an existing file. It should fail.
    EXPECT_EQ( FileMonitor::addFile(getPath("existing.txt"), &listener), 1 ) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;

    waitForFileEvents();
    FileMonitor::updates(2) ;

    // The listener should be notified 1 times with the same event.
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, noUpdate_test)
{
    MyFileListener listener ;

    // Add an existing file.It should work.
    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, updateNoChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    waitForFileEvents();
    FileMonitor::updates(2) ;
    EXPECT_EQ( listener.m_files.size(), 0u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChange_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    //waitForFileEvents();
    //FileMonitor::updates(2) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;
    waitForFileEvents();
    FileMonitor::updates(2) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileChangeTwice_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath("existing.txt"), &listener) ;
    //FileMonitor::updates(2) ;

    // change the file content 2x to test if the events are coalesced.
    listener.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 100) ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    waitForFileEvents();
    FileMonitor::updates(2) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}

TEST(FileMonitor, fileListenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    //FileMonitor::updates(2) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeFileListener(getPath("existing.txt"), &listener1) ;

    waitForFileEvents();
    FileMonitor::updates(2) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, listenerRemoved_test)
{
    MyFileListener listener1 ;
    MyFileListener listener2 ;

    FileMonitor::addFile(getPath("existing.txt"), &listener1) ;
    FileMonitor::addFile(getPath("existing.txt"), &listener2) ;
    //FileMonitor::updates(2) ;

    // change the file content 2x to test if the events are coalesced.
    listener1.m_files.clear() ;
    listener2.m_files.clear() ;
    createAFilledFile(getPath("existing.txt"), 200) ;

    FileMonitor::removeListener(&listener1) ;

    waitForFileEvents();
    FileMonitor::updates(2) ;
    EXPECT_EQ( listener1.m_files.size(), 0u) ;
    EXPECT_EQ( listener2.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener1) ;
    FileMonitor::removeListener(&listener2) ;
}

TEST(FileMonitor, fileChange2_test)
{
    MyFileListener listener ;

    FileMonitor::addFile(getPath(""),"existing.txt", &listener) ;
    //waitForFileEvents();
    //FileMonitor::updates(2) ;

    // change the file content..
    createAFilledFile(getPath("existing.txt"), 10) ;

    waitForFileEvents();
    FileMonitor::updates(2) ;
    EXPECT_EQ( listener.m_files.size(), 1u) ;

    FileMonitor::removeListener(&listener) ;
}
<|endoftext|>


<commit_before>// Copyright 2016-2019 Chris Conway (Koderz). All Rights Reserved.

#include "RuntimeMeshStaticMeshConverter.h"

#include "EngineGlobals.h"
#include "Engine/StaticMesh.h"
#include "PhysicsEngine/BodySetup.h"

int32 URuntimeMeshStaticMeshConverter::CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, FRuntimeMeshRenderableMeshData& NewMeshData)
{
	int32* FoundIndex = MeshToSectionVertexMap.Find(VertexIndex);
	if (FoundIndex != nullptr)
	{
		return *FoundIndex;
	}
	else
	{
		// Copy Position
		int32 NewVertexIndex = NewMeshData.Positions.Add(LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex));

		// Copy Tangents
		NewMeshData.Tangents.Add(
			LOD.VertexBuffers.StaticMeshVertexBuffer.VertexTangentX(VertexIndex),
			LOD.VertexBuffers.StaticMeshVertexBuffer.VertexTangentY(VertexIndex),
			LOD.VertexBuffers.StaticMeshVertexBuffer.VertexTangentZ(VertexIndex));

		// Copy UV's
		for (uint32 UVIndex = 0; UVIndex < LOD.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(); UVIndex++)
		{
			NewMeshData.TexCoords.Add(LOD.VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, UVIndex), UVIndex);
		}

		// Copy Color
		if (LOD.bHasColorVertexData)
		{
			NewMeshData.Colors.Add(LOD.VertexBuffers.ColorVertexBuffer.VertexColor(VertexIndex));
		}

		MeshToSectionVertexMap.Add(VertexIndex, NewVertexIndex);
		return NewVertexIndex;
	}
}

int32 URuntimeMeshStaticMeshConverter::CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, int32 NumUVChannels, FRuntimeMeshCollisionData& NewMeshData)
{
	int32* FoundIndex = MeshToSectionVertexMap.Find(VertexIndex);
	if (FoundIndex != nullptr)
	{
		return *FoundIndex;
	}
	else
	{
		// Copy Position
		int32 NewVertexIndex = NewMeshData.Vertices.Add(LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex));

		// Copy UV's
		for (int32 UVIndex = 0; UVIndex < NumUVChannels; UVIndex++)
		{
			NewMeshData.TexCoords.Add(UVIndex, LOD.VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, UVIndex));
		}

		MeshToSectionVertexMap.Add(VertexIndex, NewVertexIndex);
		return NewVertexIndex;
	}
}

bool URuntimeMeshStaticMeshConverter::CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex, int32 SectionId, FRuntimeMeshRenderableMeshData& OutMeshData)
{
	// Check valid static mesh
	if (StaticMesh == nullptr || StaticMesh->IsPendingKill())
	{
		return false;
	}

	// Check mesh data is accessible
	if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->RenderData != nullptr))
	{
		return false;
	}

	// Check valid LOD
	if (!StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
	{
		return false;
	}

	const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[LODIndex];

	// Check valid section
	if (!LOD.Sections.IsValidIndex(SectionId))
	{
		return false;
	}

	OutMeshData = FRuntimeMeshRenderableMeshData(
		LOD.VertexBuffers.StaticMeshVertexBuffer.GetUseHighPrecisionTangentBasis(),
		LOD.VertexBuffers.StaticMeshVertexBuffer.GetUseFullPrecisionUVs(),
		LOD.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(),
		LOD.IndexBuffer.Is32Bit());

	// Map from vert buffer for whole mesh to vert buffer for section of interest
	TMap<int32, int32> MeshToSectionVertMap;

	const FStaticMeshSection& Section = LOD.Sections[SectionId];
	const uint32 OnePastLastIndex = Section.FirstIndex + Section.NumTriangles * 3;
	FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();

	// Iterate over section index buffer, copying verts as needed
	for (uint32 i = Section.FirstIndex; i < OnePastLastIndex; i++)
	{
		uint32 MeshVertIndex = Indices[i];

		// See if we have this vert already in our section vert buffer, and copy vert in if not
		int32 SectionVertIndex = CopyVertexOrGetIndex(LOD, Section, MeshToSectionVertMap, MeshVertIndex, OutMeshData);

		// Add to index buffer
		OutMeshData.Triangles.Add(SectionVertIndex);
	}

	// Lets copy the adjacency information too for tessellation
	// At this point all vertices should be copied so it should work to just copy/convert the indices.
	if (LOD.bHasAdjacencyInfo && LOD.AdditionalIndexBuffers->AdjacencyIndexBuffer.GetNumIndices() > 0)
	{
		FIndexArrayView AdjacencyIndices = LOD.AdditionalIndexBuffers->AdjacencyIndexBuffer.GetArrayView();

		// We multiply these by 4 as the adjacency data is 12 indices per triangle instead of the normal 3
		uint32 StartIndex = Section.FirstIndex * 4;
		uint32 NumIndices = Section.NumTriangles * 3 * 4;

		for (uint32 Index = 0; Index < NumIndices; Index++)
		{
			OutMeshData.AdjacencyTriangles.Add(MeshToSectionVertMap[AdjacencyIndices[Index]]);
		}
	}

	return true;
}

bool URuntimeMeshStaticMeshConverter::CopyStaticMeshCollisionToCollisionSettings(UStaticMesh* StaticMesh, FRuntimeMeshCollisionSettings& OutCollisionSettings)
{
	// Check valid static mesh
	if (StaticMesh == nullptr || StaticMesh->IsPendingKill())
	{
		return false;
	}

	// Check mesh data is accessible
	if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->RenderData != nullptr))
	{
		return false;
	}

	// Do we have a body setup to copy?
	if (StaticMesh->BodySetup == nullptr)
	{
		return false;
	}

	// Copy convex elements
	const auto& SourceConvexElems = StaticMesh->BodySetup->AggGeom.ConvexElems;
	for (int32 ConvexIndex = 0; ConvexIndex < SourceConvexElems.Num(); ConvexIndex++)
	{
		OutCollisionSettings.ConvexElements.Emplace(
			SourceConvexElems[ConvexIndex].VertexData,
			SourceConvexElems[ConvexIndex].ElemBox);
	}

	// Copy boxes
	const auto& SourceBoxes = StaticMesh->BodySetup->AggGeom.BoxElems;
	for (int32 BoxIndex = 0; BoxIndex < SourceBoxes.Num(); BoxIndex++)
	{
		OutCollisionSettings.Boxes.Emplace(
			SourceBoxes[BoxIndex].Center,
			SourceBoxes[BoxIndex].Rotation,
			SourceBoxes[BoxIndex].X,
			SourceBoxes[BoxIndex].Y,
			SourceBoxes[BoxIndex].Z);
	}

	// Copy spheres
	const auto& SourceSpheres = StaticMesh->BodySetup->AggGeom.SphereElems;
	for (int32 SphereIndex = 0; SphereIndex < SourceSpheres.Num(); SphereIndex++)
	{
		OutCollisionSettings.Spheres.Emplace(
			SourceSpheres[SphereIndex].Center,
			SourceSpheres[SphereIndex].Radius);
	}

	// Copy capsules
	const auto& SourceCapsules = StaticMesh->BodySetup->AggGeom.SphylElems;
	for (int32 CapsuleIndex = 0; CapsuleIndex < SourceCapsules.Num(); CapsuleIndex++)
	{
		OutCollisionSettings.Capsules.Emplace(
			SourceCapsules[CapsuleIndex].Center,
			SourceCapsules[CapsuleIndex].Rotation,
			SourceCapsules[CapsuleIndex].Radius,
			SourceCapsules[CapsuleIndex].Length);
	}

	return true;
}

bool URuntimeMeshStaticMeshConverter::CopyStaticMeshLODToCollisionData(UStaticMesh* StaticMesh, int32 LODIndex, FRuntimeMeshCollisionData& OutCollisionData)
{
	// Check valid static mesh
	if (StaticMesh == nullptr || StaticMesh->IsPendingKill())
	{
		return false;
	}

	// Check mesh data is accessible
	if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->RenderData != nullptr))
	{
		return false;
	}

	// Check valid LOD
	if (!StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
	{
		return false;
	}

	const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[LODIndex];

	uint32 NumUVChannels = LOD.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords();

	OutCollisionData.Vertices.Empty();
	OutCollisionData.TexCoords.Empty();
	OutCollisionData.MaterialIndices.Empty();
	OutCollisionData.Triangles.Empty();
	OutCollisionData.TexCoords.SetNumChannels(NumUVChannels);

	// Map from vert buffer for whole mesh to vert buffer for section of interest
	TMap<int32, int32> MeshToSectionVertMap;

	int32 TempIndices[3] = { 0, 0, 0 };

	int32 SectionId = 0;
	for (const FStaticMeshSection& Section : LOD.Sections)
	{
		const uint32 OnePastLastIndex = Section.FirstIndex + Section.NumTriangles;
		FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();

		int32 StartTriangle = OutCollisionData.Triangles.Num();

		// Iterate over section index buffer, copying verts as needed
		for (uint32 TriIndex = Section.FirstIndex; TriIndex < OnePastLastIndex; TriIndex++)
		{
			for (int32 Index = 0; Index < 3; Index++)
			{
				uint32 MeshVertIndex = Indices[TriIndex * 3 + Index];

				// See if we have this vert already in our section vert buffer, and copy vert in if not
				int32 SectionVertIndex = CopyVertexOrGetIndex(LOD, Section, MeshToSectionVertMap, MeshVertIndex, NumUVChannels, OutCollisionData);

				TempIndices[Index] = MeshVertIndex;
			}

			// Add to index buffer
			OutCollisionData.Triangles.Add(TempIndices[0], TempIndices[1], TempIndices[2]);
			OutCollisionData.MaterialIndices.Add(Section.MaterialIndex);
		}

		// Add the collision section
		OutCollisionData.CollisionSources.Emplace(StartTriangle, OutCollisionData.Triangles.Num() - 1, nullptr, SectionId, ERuntimeMeshCollisionFaceSourceType::Renderable);

		SectionId++;
	}

	return true;
}

<commit_msg>Fix `AdjacencyTriangles` bug<commit_after>// Copyright 2016-2019 Chris Conway (Koderz). All Rights Reserved.

#include "RuntimeMeshStaticMeshConverter.h"

#include "EngineGlobals.h"
#include "Engine/StaticMesh.h"
#include "PhysicsEngine/BodySetup.h"

int32 URuntimeMeshStaticMeshConverter::CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, FRuntimeMeshRenderableMeshData& NewMeshData)
{
	int32* FoundIndex = MeshToSectionVertexMap.Find(VertexIndex);
	if (FoundIndex != nullptr)
	{
		return *FoundIndex;
	}
	else
	{
		// Copy Position
		int32 NewVertexIndex = NewMeshData.Positions.Add(LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex));

		// Copy Tangents
		NewMeshData.Tangents.Add(
			LOD.VertexBuffers.StaticMeshVertexBuffer.VertexTangentX(VertexIndex),
			LOD.VertexBuffers.StaticMeshVertexBuffer.VertexTangentY(VertexIndex),
			LOD.VertexBuffers.StaticMeshVertexBuffer.VertexTangentZ(VertexIndex));

		// Copy UV's
		for (uint32 UVIndex = 0; UVIndex < LOD.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(); UVIndex++)
		{
			NewMeshData.TexCoords.Add(LOD.VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, UVIndex), UVIndex);
		}

		// Copy Color
		if (LOD.bHasColorVertexData)
		{
			NewMeshData.Colors.Add(LOD.VertexBuffers.ColorVertexBuffer.VertexColor(VertexIndex));
		}

		MeshToSectionVertexMap.Add(VertexIndex, NewVertexIndex);
		return NewVertexIndex;
	}
}

int32 URuntimeMeshStaticMeshConverter::CopyVertexOrGetIndex(const FStaticMeshLODResources& LOD, const FStaticMeshSection& Section, TMap<int32, int32>& MeshToSectionVertexMap, int32 VertexIndex, int32 NumUVChannels, FRuntimeMeshCollisionData& NewMeshData)
{
	int32* FoundIndex = MeshToSectionVertexMap.Find(VertexIndex);
	if (FoundIndex != nullptr)
	{
		return *FoundIndex;
	}
	else
	{
		// Copy Position
		int32 NewVertexIndex = NewMeshData.Vertices.Add(LOD.VertexBuffers.PositionVertexBuffer.VertexPosition(VertexIndex));

		// Copy UV's
		for (int32 UVIndex = 0; UVIndex < NumUVChannels; UVIndex++)
		{
			NewMeshData.TexCoords.Add(UVIndex, LOD.VertexBuffers.StaticMeshVertexBuffer.GetVertexUV(VertexIndex, UVIndex));
		}

		MeshToSectionVertexMap.Add(VertexIndex, NewVertexIndex);
		return NewVertexIndex;
	}
}

bool URuntimeMeshStaticMeshConverter::CopyStaticMeshSectionToRenderableMeshData(UStaticMesh* StaticMesh, int32 LODIndex, int32 SectionId, FRuntimeMeshRenderableMeshData& OutMeshData)
{
	// Check valid static mesh
	if (StaticMesh == nullptr || StaticMesh->IsPendingKill())
	{
		return false;
	}

	// Check mesh data is accessible
	if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->RenderData != nullptr))
	{
		return false;
	}

	// Check valid LOD
	if (!StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
	{
		return false;
	}

	const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[LODIndex];

	// Check valid section
	if (!LOD.Sections.IsValidIndex(SectionId))
	{
		return false;
	}

	OutMeshData = FRuntimeMeshRenderableMeshData(
		LOD.VertexBuffers.StaticMeshVertexBuffer.GetUseHighPrecisionTangentBasis(),
		LOD.VertexBuffers.StaticMeshVertexBuffer.GetUseFullPrecisionUVs(),
		LOD.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords(),
		LOD.IndexBuffer.Is32Bit());

	// Map from vert buffer for whole mesh to vert buffer for section of interest
	TMap<int32, int32> MeshToSectionVertMap;

	const FStaticMeshSection& Section = LOD.Sections[SectionId];
	const uint32 OnePastLastIndex = Section.FirstIndex + Section.NumTriangles * 3;
	FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();

	// Iterate over section index buffer, copying verts as needed
	for (uint32 i = Section.FirstIndex; i < OnePastLastIndex; i++)
	{
		uint32 MeshVertIndex = Indices[i];

		// See if we have this vert already in our section vert buffer, and copy vert in if not
		int32 SectionVertIndex = CopyVertexOrGetIndex(LOD, Section, MeshToSectionVertMap, MeshVertIndex, OutMeshData);

		// Add to index buffer
		OutMeshData.Triangles.Add(SectionVertIndex);
	}

	// Lets copy the adjacency information too for tessellation
	// At this point all vertices should be copied so it should work to just copy/convert the indices.
	if (LOD.bHasAdjacencyInfo && LOD.AdditionalIndexBuffers->AdjacencyIndexBuffer.GetNumIndices() > 0)
	{
		FIndexArrayView AdjacencyIndices = LOD.AdditionalIndexBuffers->AdjacencyIndexBuffer.GetArrayView();

		// We multiply these by 4 as the adjacency data is 12 indices per triangle instead of the normal 3
		uint32 StartIndex = Section.FirstIndex * 4;
		uint32 NumIndices = Section.NumTriangles * 3 * 4;

		for (uint32 Index = 0; Index < NumIndices; Index++)
		{
			OutMeshData.AdjacencyTriangles.Add(MeshToSectionVertMap[AdjacencyIndices[StartIndex + Index]]);
		}
	}

	return true;
}

bool URuntimeMeshStaticMeshConverter::CopyStaticMeshCollisionToCollisionSettings(UStaticMesh* StaticMesh, FRuntimeMeshCollisionSettings& OutCollisionSettings)
{
	// Check valid static mesh
	if (StaticMesh == nullptr || StaticMesh->IsPendingKill())
	{
		return false;
	}

	// Check mesh data is accessible
	if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->RenderData != nullptr))
	{
		return false;
	}

	// Do we have a body setup to copy?
	if (StaticMesh->BodySetup == nullptr)
	{
		return false;
	}

	// Copy convex elements
	const auto& SourceConvexElems = StaticMesh->BodySetup->AggGeom.ConvexElems;
	for (int32 ConvexIndex = 0; ConvexIndex < SourceConvexElems.Num(); ConvexIndex++)
	{
		OutCollisionSettings.ConvexElements.Emplace(
			SourceConvexElems[ConvexIndex].VertexData,
			SourceConvexElems[ConvexIndex].ElemBox);
	}

	// Copy boxes
	const auto& SourceBoxes = StaticMesh->BodySetup->AggGeom.BoxElems;
	for (int32 BoxIndex = 0; BoxIndex < SourceBoxes.Num(); BoxIndex++)
	{
		OutCollisionSettings.Boxes.Emplace(
			SourceBoxes[BoxIndex].Center,
			SourceBoxes[BoxIndex].Rotation,
			SourceBoxes[BoxIndex].X,
			SourceBoxes[BoxIndex].Y,
			SourceBoxes[BoxIndex].Z);
	}

	// Copy spheres
	const auto& SourceSpheres = StaticMesh->BodySetup->AggGeom.SphereElems;
	for (int32 SphereIndex = 0; SphereIndex < SourceSpheres.Num(); SphereIndex++)
	{
		OutCollisionSettings.Spheres.Emplace(
			SourceSpheres[SphereIndex].Center,
			SourceSpheres[SphereIndex].Radius);
	}

	// Copy capsules
	const auto& SourceCapsules = StaticMesh->BodySetup->AggGeom.SphylElems;
	for (int32 CapsuleIndex = 0; CapsuleIndex < SourceCapsules.Num(); CapsuleIndex++)
	{
		OutCollisionSettings.Capsules.Emplace(
			SourceCapsules[CapsuleIndex].Center,
			SourceCapsules[CapsuleIndex].Rotation,
			SourceCapsules[CapsuleIndex].Radius,
			SourceCapsules[CapsuleIndex].Length);
	}

	return true;
}

bool URuntimeMeshStaticMeshConverter::CopyStaticMeshLODToCollisionData(UStaticMesh* StaticMesh, int32 LODIndex, FRuntimeMeshCollisionData& OutCollisionData)
{
	// Check valid static mesh
	if (StaticMesh == nullptr || StaticMesh->IsPendingKill())
	{
		return false;
	}

	// Check mesh data is accessible
	if (!((GIsEditor || StaticMesh->bAllowCPUAccess) && StaticMesh->RenderData != nullptr))
	{
		return false;
	}

	// Check valid LOD
	if (!StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
	{
		return false;
	}

	const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[LODIndex];

	uint32 NumUVChannels = LOD.VertexBuffers.StaticMeshVertexBuffer.GetNumTexCoords();

	OutCollisionData.Vertices.Empty();
	OutCollisionData.TexCoords.Empty();
	OutCollisionData.MaterialIndices.Empty();
	OutCollisionData.Triangles.Empty();
	OutCollisionData.TexCoords.SetNumChannels(NumUVChannels);

	// Map from vert buffer for whole mesh to vert buffer for section of interest
	TMap<int32, int32> MeshToSectionVertMap;

	int32 TempIndices[3] = { 0, 0, 0 };

	int32 SectionId = 0;
	for (const FStaticMeshSection& Section : LOD.Sections)
	{
		const uint32 OnePastLastIndex = Section.FirstIndex + Section.NumTriangles;
		FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();

		int32 StartTriangle = OutCollisionData.Triangles.Num();

		// Iterate over section index buffer, copying verts as needed
		for (uint32 TriIndex = Section.FirstIndex; TriIndex < OnePastLastIndex; TriIndex++)
		{
			for (int32 Index = 0; Index < 3; Index++)
			{
				uint32 MeshVertIndex = Indices[TriIndex * 3 + Index];

				// See if we have this vert already in our section vert buffer, and copy vert in if not
				int32 SectionVertIndex = CopyVertexOrGetIndex(LOD, Section, MeshToSectionVertMap, MeshVertIndex, NumUVChannels, OutCollisionData);

				TempIndices[Index] = MeshVertIndex;
			}

			// Add to index buffer
			OutCollisionData.Triangles.Add(TempIndices[0], TempIndices[1], TempIndices[2]);
			OutCollisionData.MaterialIndices.Add(Section.MaterialIndex);
		}

		// Add the collision section
		OutCollisionData.CollisionSources.Emplace(StartTriangle, OutCollisionData.Triangles.Num() - 1, nullptr, SectionId, ERuntimeMeshCollisionFaceSourceType::Renderable);

		SectionId++;
	}

	return true;
}

<|endoftext|>


<commit_before>/*
** Copyright 2014 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker 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
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <sstream>
#include <QVariant>
#include <QSet>
#include <QSqlError>
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/logging/logging.hh"
#include "com/centreon/broker/notification/loaders/contactgroup_loader.hh"

using namespace com::centreon::broker::notification;
using namespace com::centreon::broker::notification::objects;

contactgroup_loader::contactgroup_loader() {}

/**
 *  Load the nodegroups from the database.
 *
 *  @param[in] db       An open connection to the database.
 *  @param[out] output  A nodegroup builder object to register the nodegroups.
 */
void contactgroup_loader::load(
                            QSqlDatabase* db, contactgroup_builder* output) {
  // If we don't have any db or output, don't do anything.
  if (!db || !output)
    return;

  logging::debug(logging::medium)
    << "notification: loading contact groups from the database";

  QSqlQuery query(*db);

  // Performance improvement, as we never go back.
  query.setForwardOnly(true);

  // Get contactgroups.
  if (!query.exec("SELECT cg_id, cg_name, cg_alias FROM cfg_contactgroups"))
    throw (exceptions::msg()
           << "notification: cannot load contact groups from database: "
           << query.lastError().text());

  while (query.next()) {
    unsigned int id = query.value(0).toUInt();
    objects::contactgroup::ptr ctg(new objects::contactgroup);
    ctg->set_name(query.value(1).toString().toStdString());
    ctg->set_alias(query.value(2).toString().toStdString());
    output->add_contactgroup(id, ctg);
  }

  // Get contactgroup contact relations.
  if (!query.exec("SELECT contact_contact_id, contactgroup_cg_id"
                  "  FROM cfg_contactgroups_contacts_relations"))
    throw (exceptions::msg()
           << "notification: cannot load memberships of contact groups "
              "from database: "
           << query.lastError().text());

  while (query.next()) {

  }
}
<commit_msg>Notification: Fix an oversight in contactgroup_loader.<commit_after>/*
** Copyright 2014 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker 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
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/

#include <sstream>
#include <QVariant>
#include <QSet>
#include <QSqlError>
#include "com/centreon/broker/exceptions/msg.hh"
#include "com/centreon/broker/logging/logging.hh"
#include "com/centreon/broker/notification/loaders/contactgroup_loader.hh"

using namespace com::centreon::broker::notification;
using namespace com::centreon::broker::notification::objects;

contactgroup_loader::contactgroup_loader() {}

/**
 *  Load the nodegroups from the database.
 *
 *  @param[in] db       An open connection to the database.
 *  @param[out] output  A nodegroup builder object to register the nodegroups.
 */
void contactgroup_loader::load(
                            QSqlDatabase* db, contactgroup_builder* output) {
  // If we don't have any db or output, don't do anything.
  if (!db || !output)
    return;

  logging::debug(logging::medium)
    << "notification: loading contact groups from the database";

  QSqlQuery query(*db);

  // Performance improvement, as we never go back.
  query.setForwardOnly(true);

  // Get contactgroups.
  if (!query.exec("SELECT cg_id, cg_name, cg_alias FROM cfg_contactgroups"))
    throw (exceptions::msg()
           << "notification: cannot load contact groups from database: "
           << query.lastError().text());

  while (query.next()) {
    unsigned int id = query.value(0).toUInt();
    objects::contactgroup::ptr ctg(new objects::contactgroup);
    ctg->set_name(query.value(1).toString().toStdString());
    ctg->set_alias(query.value(2).toString().toStdString());
    output->add_contactgroup(id, ctg);
  }

  // Get contactgroup contact relations.
  if (!query.exec("SELECT contact_contact_id, contactgroup_cg_id"
                  "  FROM cfg_contactgroups_contacts_relations"))
    throw (exceptions::msg()
           << "notification: cannot load memberships of contact groups "
              "from database: "
           << query.lastError().text());

  while (query.next())
    output->add_contactgroup_contact_relation(
              query.value(0).toUInt(), query.value(1).toUInt());
}
<|endoftext|>


<commit_before><commit_msg>Update sample for latest changes.<commit_after><|endoftext|>


<commit_before><commit_msg>MS:<commit_after><|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

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

#include "mitkContourModelLiveWireInteractor.h"

#include "mitkToolManager.h"

#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"

#include <mitkPositionEvent.h>
#include <mitkStateEvent.h>

#include <mitkInteractionConst.h>

mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor(DataNode* dataNode)
:ContourModelInteractor(dataNode)
{
  m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New();

  mitk::ContourModel::Pointer m_ContourLeft = mitk::ContourModel::New();
  mitk::ContourModel::Pointer m_ContourRight = mitk::ContourModel::New();
  m_NextActiveVertexDown.Fill(0);
  m_NextActiveVertexUp.Fill(0);
}

mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor()
{
}

bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick( Action* action, const StateEvent* stateEvent)
{
  const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) {
    this->HandleEvent( new mitk::StateEvent(EIDNO, stateEvent->GetEvent()) );
    return false;
  }

  mitk::StateEvent* newStateEvent = NULL;

  int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep();

  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>(
    m_DataNode->GetData() );

  contour->Deselect();

  /*
  * Check distance to any vertex.
  * Transition YES if click close to a vertex
  */
  mitk::Point3D click = positionEvent->GetWorldPosition();

  if (contour->SelectVertexAt(click, 1.5, timestep) )
  {
    contour->SetSelectedVertexAsControlPoint();

    assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );
    mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );
    newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent());
    m_lastMousePosition = click;

    mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>( m_DataNode->GetData() );

    //
    m_ContourLeft = mitk::ContourModel::New();

    //get coordinates of next active vertex downwards from selected vertex
    int downIndex = SplitContourFromSelectedVertex( contour, m_ContourLeft, false, timestep);

    mitk::ContourModel::VertexIterator itDown = contour->IteratorBegin() + downIndex;
    m_NextActiveVertexDown = (*itDown)->Coordinates;

    //
    m_ContourRight = mitk::ContourModel::New();

    //get coordinates of next active vertex upwards from selected vertex
    int upIndex = SplitContourFromSelectedVertex( contour, m_ContourRight, true, timestep);

    mitk::ContourModel::VertexIterator itUp = contour->IteratorBegin() + upIndex;
    m_NextActiveVertexUp = (*itUp)->Coordinates;

  }
  else
  {
    newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent());
  }

  this->HandleEvent( newStateEvent );

  return true;
}

bool mitk::ContourModelLiveWireInteractor::OnDeletePoint( Action* action, const StateEvent* stateEvent)
{

  int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep();

  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>( m_DataNode->GetData() );

  if(contour->GetSelectedVertex())
  {

  mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
  newContour->Expand(contour->GetTimeSteps());
  newContour->Concatenate( m_ContourLeft, timestep );

  //recompute contour between neighbored two active control points
  this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown );
  this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp );
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer liveWireContour = mitk::ContourModel::New();
  liveWireContour = this->m_LiveWireFilter->GetOutput();

  //insert new liveWire computed points
  newContour->Concatenate( liveWireContour, timestep );

  newContour->Concatenate( m_ContourRight, timestep );

  newContour->SetIsClosed(contour->IsClosed(timestep), timestep);

  m_DataNode->SetData(newContour);

  assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );
  mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );

  }
  return true;
}

bool mitk::ContourModelLiveWireInteractor::OnMovePoint( Action* action, const StateEvent* stateEvent)
{
  const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) return false;

  int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep();

  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>( m_DataNode->GetData() );

  mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
  newContour->Expand(contour->GetTimeSteps());

  /*++++++++++++++ concatenate LEFT ++++++++++++++++++++++++++++++*/
  newContour->Concatenate( m_ContourLeft, timestep );

  mitk::Point3D currentPosition = positionEvent->GetWorldPosition();

  /*+++++++++++++++ start computation ++++++++++++++++++++*/
  //recompute contour between previous active vertex and selected vertex
  this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown );
  this->m_LiveWireFilter->SetEndPoint( currentPosition );
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer liveWireContour = mitk::ContourModel::New();
  liveWireContour = this->m_LiveWireFilter->GetOutput();

  //remove point at current position because it is included in next livewire segment too.
  liveWireContour->RemoveVertexAt( liveWireContour->GetNumberOfVertices(timestep) - 1, timestep);

  /*++++++++++++++ concatenate LIVEWIRE ++++++++++++++++++++++++++++++*/
  //insert new liveWire computed points
  newContour->Concatenate( liveWireContour, timestep );

  //recompute contour between selected vertex and next active vertex
  this->m_LiveWireFilter->SetStartPoint( currentPosition );
  this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp );
  this->m_LiveWireFilter->Update();

  liveWireContour = this->m_LiveWireFilter->GetOutput();

  //make point at mouse position active again, so it is drawn
  const_cast<mitk::ContourModel::VertexType*>( liveWireContour->GetVertexAt(0, timestep) )->IsControlPoint = true;

  /*++++++++++++++ concatenate RIGHT ++++++++++++++++++++++++++++++*/
  //insert new liveWire computed points
  newContour->Concatenate( liveWireContour, timestep );
  /*------------------------------------------------*/

  newContour->Concatenate( m_ContourRight, timestep );

  newContour->SetIsClosed(contour->IsClosed(timestep), timestep);
  newContour->Deselect();//just to make sure
  m_DataNode->SetData(newContour);

  this->m_lastMousePosition = positionEvent->GetWorldPosition();

  assert( positionEvent->GetSender()->GetRenderWindow() );
  mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() );

  return true;
}

int mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel* sourceContour, mitk::ContourModel* destinationContour, bool fromSelectedUpwards, int timestep)
{

  mitk::ContourModel::VertexIterator end =sourceContour->IteratorEnd();
  mitk::ContourModel::VertexIterator begin =sourceContour->IteratorBegin();

  //search next active control point to left and rigth and set as start and end point for filter
  mitk::ContourModel::VertexIterator itSelected = sourceContour->IteratorBegin();

  //move iterator to position
  while((*itSelected) != sourceContour->GetSelectedVertex())
  {
    itSelected++;
  }

  if(fromSelectedUpwards)
  {
    mitk::ContourModel::VertexIterator itUp = itSelected;

    if(itUp != end)
    {
      itUp++;//step once up otherwise the the loop breaks immediately
    }

    while( itUp != end && !((*itUp)->IsControlPoint)){ itUp++; }

    mitk::ContourModel::VertexIterator it = itUp;

    //copy the rest of the original contour
    while(it != end)
    {
      destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
      it++;
    }

    //return the offset of iterator at one before next-vertex-upwards
    if(itUp != begin)
    {
      return itUp._Myoff - 1;
    }
    else
    {
      return itUp._Myoff;
    }

  }
  else //downwards
  {
    mitk::ContourModel::VertexIterator itDown = itSelected;

    if(itDown != begin)
    {
      itDown--;//step once down otherwise the the loop breaks immediately
    }

    while( itDown != begin && !((*itDown)->IsControlPoint)){ itDown--; }

    mitk::ContourModel::VertexIterator it = sourceContour->IteratorBegin();

    if(it != end)//if not empty
    {
      //always add the first vertex
      destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
      it++;
    }

    //copy from begin to itDown
    while(it <= itDown)
    {
      destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
      it++;
    }

    //add vertex at itDown - it's not considered during while loop
    if( it != begin && it != end)
    {
      //destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
    }

    //return the offset of iterator at one after next-vertex-downwards
    if( itDown != end)
    {
      return itDown._Myoff + 1;//index of next vertex
    }
    else
    {
      return itDown._Myoff - 1;
    }
  }
}
<commit_msg>Fix deleting and moving first controlpoint.<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

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

#include "mitkContourModelLiveWireInteractor.h"

#include "mitkToolManager.h"

#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"

#include <mitkPositionEvent.h>
#include <mitkStateEvent.h>

#include <mitkInteractionConst.h>

mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor(DataNode* dataNode)
:ContourModelInteractor(dataNode)
{
  m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New();

  mitk::ContourModel::Pointer m_ContourLeft = mitk::ContourModel::New();
  mitk::ContourModel::Pointer m_ContourRight = mitk::ContourModel::New();
  m_NextActiveVertexDown.Fill(0);
  m_NextActiveVertexUp.Fill(0);
}

mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor()
{
}

bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick( Action* action, const StateEvent* stateEvent)
{
  const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) {
    this->HandleEvent( new mitk::StateEvent(EIDNO, stateEvent->GetEvent()) );
    return false;
  }

  mitk::StateEvent* newStateEvent = NULL;

  int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep();

  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>(
    m_DataNode->GetData() );

  contour->Deselect();

  /*
  * Check distance to any vertex.
  * Transition YES if click close to a vertex
  */
  mitk::Point3D click = positionEvent->GetWorldPosition();

  if (contour->SelectVertexAt(click, 1.5, timestep) )
  {
    contour->SetSelectedVertexAsControlPoint();

    assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );
    mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );
    newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent());
    m_lastMousePosition = click;

    mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>( m_DataNode->GetData() );

    //
    m_ContourLeft = mitk::ContourModel::New();

    //get coordinates of next active vertex downwards from selected vertex
    int downIndex = SplitContourFromSelectedVertex( contour, m_ContourLeft, false, timestep);

    mitk::ContourModel::VertexIterator itDown = contour->IteratorBegin() + downIndex;
    m_NextActiveVertexDown = (*itDown)->Coordinates;

    //
    m_ContourRight = mitk::ContourModel::New();

    //get coordinates of next active vertex upwards from selected vertex
    int upIndex = SplitContourFromSelectedVertex( contour, m_ContourRight, true, timestep);

    mitk::ContourModel::VertexIterator itUp = contour->IteratorBegin() + upIndex;
    m_NextActiveVertexUp = (*itUp)->Coordinates;

  }
  else
  {
    newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent());
  }

  this->HandleEvent( newStateEvent );

  return true;
}

bool mitk::ContourModelLiveWireInteractor::OnDeletePoint( Action* action, const StateEvent* stateEvent)
{

  int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep();

  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>( m_DataNode->GetData() );

  if(contour->GetSelectedVertex())
  {

    mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
    newContour->Expand(contour->GetTimeSteps());
    newContour->Concatenate( m_ContourLeft, timestep );

    //recompute contour between neighbored two active control points
    this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown );
    this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp );
    this->m_LiveWireFilter->Update();

    mitk::ContourModel::Pointer liveWireContour = mitk::ContourModel::New();
    liveWireContour = this->m_LiveWireFilter->GetOutput();

    //insert new liveWire computed points
    newContour->Concatenate( liveWireContour, timestep );

    newContour->Concatenate( m_ContourRight, timestep );

    newContour->SetIsClosed(contour->IsClosed(timestep), timestep);

    m_DataNode->SetData(newContour);

    assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );
    mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() );

  }
  return true;
}

bool mitk::ContourModelLiveWireInteractor::OnMovePoint( Action* action, const StateEvent* stateEvent)
{
  const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent());
  if (!positionEvent) return false;

  int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep();

  mitk::ContourModel *contour = dynamic_cast<mitk::ContourModel *>( m_DataNode->GetData() );

  mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
  newContour->Expand(contour->GetTimeSteps());

  /*++++++++++++++ concatenate LEFT ++++++++++++++++++++++++++++++*/
  newContour->Concatenate( m_ContourLeft, timestep );

  mitk::Point3D currentPosition = positionEvent->GetWorldPosition();

  /*+++++++++++++++ start computation ++++++++++++++++++++*/
  //recompute contour between previous active vertex and selected vertex
  this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown );
  this->m_LiveWireFilter->SetEndPoint( currentPosition );
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer liveWireContour = mitk::ContourModel::New();
  liveWireContour = this->m_LiveWireFilter->GetOutput();

  //remove point at current position because it is included in next livewire segment too.
  liveWireContour->RemoveVertexAt( liveWireContour->GetNumberOfVertices(timestep) - 1, timestep);

  /*++++++++++++++ concatenate LIVEWIRE ++++++++++++++++++++++++++++++*/
  //insert new liveWire computed points
  newContour->Concatenate( liveWireContour, timestep );

  //recompute contour between selected vertex and next active vertex
  this->m_LiveWireFilter->SetStartPoint( currentPosition );
  this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp );
  this->m_LiveWireFilter->Update();

  liveWireContour = this->m_LiveWireFilter->GetOutput();

  //make point at mouse position active again, so it is drawn
  const_cast<mitk::ContourModel::VertexType*>( liveWireContour->GetVertexAt(0, timestep) )->IsControlPoint = true;

  /*++++++++++++++ concatenate RIGHT ++++++++++++++++++++++++++++++*/
  //insert new liveWire computed points
  newContour->Concatenate( liveWireContour, timestep );
  /*------------------------------------------------*/

  newContour->Concatenate( m_ContourRight, timestep );

  newContour->SetIsClosed(contour->IsClosed(timestep), timestep);
  newContour->Deselect();//just to make sure
  m_DataNode->SetData(newContour);

  this->m_lastMousePosition = positionEvent->GetWorldPosition();

  assert( positionEvent->GetSender()->GetRenderWindow() );
  mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() );

  return true;
}

int mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel* sourceContour, mitk::ContourModel* destinationContour, bool fromSelectedUpwards, int timestep)
{

  mitk::ContourModel::VertexIterator end =sourceContour->IteratorEnd();
  mitk::ContourModel::VertexIterator begin =sourceContour->IteratorBegin();

  //search next active control point to left and rigth and set as start and end point for filter
  mitk::ContourModel::VertexIterator itSelected = sourceContour->IteratorBegin();

  //move iterator to position
  while((*itSelected) != sourceContour->GetSelectedVertex())
  {
    itSelected++;
  }

  //CASE search upwards for next control point
  if(fromSelectedUpwards)
  {
    mitk::ContourModel::VertexIterator itUp = itSelected;

    if(itUp != end)
    {
      itUp++;//step once up otherwise the the loop breaks immediately
    }

    while( itUp != end && !((*itUp)->IsControlPoint)){ itUp++; }

    mitk::ContourModel::VertexIterator it = itUp;

    if(itSelected != begin)
    {
      //copy the rest of the original contour
      while(it != end)
      {
        destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
        it++;
      }
    }
    //else do not copy the contour

    //return the offset of iterator at one before next-vertex-upwards
    if(itUp != begin)
    {
      return itUp._Myoff - 1;
    }
    else
    {
      return itUp._Myoff;
    }

  }
  else //CASE search downwards for next control point
  {
    mitk::ContourModel::VertexIterator itDown = itSelected;
    mitk::ContourModel::VertexIterator it = sourceContour->IteratorBegin();

    if( itSelected != begin )
    {
      if(itDown != begin)
      {
        itDown--;//step once down otherwise the the loop breaks immediately
      }

      while( itDown != begin && !((*itDown)->IsControlPoint)){ itDown--; }

      if(it != end)//if not empty
      {
        //always add the first vertex
        destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
        it++;
      }
      //copy from begin to itDown
      while(it <= itDown)
      {
        destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
        it++;
      }
    }
    else
    {
      //if selected vertex is the first element search from end of contour downwards
      itDown = end;
      itDown--;
      while(!((*itDown)->IsControlPoint)){itDown--;}

      //move one forward as we don't want the first control point
      it++;
      //move iterator to second control point
      while( (it!=end) && !((*it)->IsControlPoint) ){it++;}
      //copy from begin to itDown
      while(it <= itDown)
      {
        //copy the contour from second control point to itDown
        destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
        it++;
      }
    }

    //add vertex at itDown - it's not considered during while loop
    if( it != begin && it != end)
    {
      //destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep);
    }

    //return the offset of iterator at one after next-vertex-downwards
    if( itDown != end)
    {
      return itDown._Myoff;// + 1;//index of next vertex
    }
    else
    {
      return itDown._Myoff - 1;
    }
  }
}
<|endoftext|>