[dali_1.1.1] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / page-turn-view / page-turn-view-impl.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-view-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <dali/public-api/animation/animation.h>
24 #include <dali/public-api/animation/constraint.h>
25 #include <dali/devel-api/events/hit-test-algorithm.h>
26 #include <dali/public-api/object/type-registry.h>
27 #include <dali/devel-api/object/type-registry-helper.h>
28 #include <dali/public-api/render-tasks/render-task-list.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
32 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-effect.h>
33 #include <dali-toolkit/internal/controls/page-turn-view/page-turn-book-spine-effect.h>
34
35 using namespace Dali;
36
37 namespace //Unnamed namespace
38 {
39 // default grid density for page turn effect, 10 pixels by 10 pixels
40 const float DEFAULT_GRID_DENSITY(10.0f);
41
42 // to bent the page, the minimal horizontal pan start position is pageSize.x * MINIMUM_START_POSITION_RATIO
43 const float MINIMUM_START_POSITION_RATIO(0.6f);
44
45 // the maximum vertical displacement of pan gesture, if exceed, will reduce it: pageSize.y * MAXIMUM_VERTICAL_MOVEMENT_RATIO
46 const float MAXIMUM_VERTICAL_MOVEMENT_RATIO(0.15f);
47
48 // when the x component of pan position reaches pageSize.x * PAGE_TURN_OVER_THRESHOLD_RATIO, page starts to turn over
49 const float PAGE_TURN_OVER_THRESHOLD_RATIO(0.5f);
50
51 // duration of animation, shorter for faster speed
52 const float PAGE_SLIDE_BACK_ANIMATION_DURATION(1.0f);
53 const float PAGE_TURN_OVER_ANIMATION_DURATION(1.2f);
54
55 // the major&minor radius (in pixels) to form an ellipse shape
56 // the top-left quarter of this ellipse is used to calculate spine normal for simulating shadow
57 const Vector2 DEFAULT_SPINE_SHADOW_PARAMETER(50.0f, 20.0f);
58
59 // constants for shadow casting
60 const float POINT_LIGHT_HEIGHT_RATIO(2.f);
61 const Vector4 DEFAULT_SHADOW_COLOR = Vector4(0.2f, 0.2f, 0.2f, 0.5f);
62
63 // constraints ////////////////////////////////////////////////////////////////
64 /**
65  * Original Center Constraint
66  *
67  * This constraint adjusts the original center property of the page turn shader effect
68  * based on the X-direction displacement of the pan gesture
69  */
70 struct OriginalCenterConstraint
71 {
72   OriginalCenterConstraint(const Vector2& originalCenter, const Vector2& offset)
73   : mOldCenter( originalCenter )
74   {
75     mNewCenter = originalCenter + offset;
76     mDistance = offset.Length() * 0.5f;
77     mDirection = offset  / mDistance;
78   }
79
80   void operator()( Vector2& current, const PropertyInputContainer& inputs )
81   {
82     float displacement = inputs[0]->GetFloat();
83
84     if( displacement < mDistance )
85     {
86       current = mOldCenter + mDirection * displacement;
87     }
88     else
89     {
90       current = mNewCenter + Vector2(0.25f*(displacement-mDistance), 0.f);
91     }
92   }
93
94   Vector2 mOldCenter;
95   Vector2 mNewCenter;
96   float mDistance;
97   Vector2 mDirection;
98 };
99
100 /**
101  * Rotation Constraint
102  *
103  * This constraint adjusts the rotation property of the page actor
104  * based on the X-direction displacement of the pan gesture
105  */
106 struct RotationConstraint
107 {
108   RotationConstraint( float distance, float pageWidth, bool isTurnBack )
109   : mDistance( distance*0.5f )
110   {
111     mStep = 1.f / pageWidth;
112     mSign = isTurnBack ? -1.0f : 1.0f;
113     mConst = isTurnBack ? -1.0f : 0.f;
114     mRotation = isTurnBack ? Quaternion( Radian( -Math::PI ), Vector3::YAXIS ) : Quaternion( Radian(0.f), Vector3::YAXIS );
115   }
116
117   void operator()( Quaternion& current, const PropertyInputContainer& inputs )
118   {
119     float displacement = inputs[0]->GetFloat();
120     if( displacement < mDistance)
121     {
122       current = mRotation;
123     }
124     else
125     {
126       float coef = std::max(-1.0f, mStep*(mDistance-displacement));
127       float angle = Math::PI * ( mConst + mSign * coef );
128       current = Quaternion( Radian( angle ), Vector3::YAXIS );
129     }
130   }
131
132   float mDistance;
133   float mStep;
134   float mConst;
135   float mSign;
136   Quaternion mRotation;
137 };
138
139 /**
140  * Current Center Constraint
141  *
142  * This constraint adjusts the current center property of the page turn shader effect
143  * based on the pan position and the original center position
144  */
145 struct CurrentCenterConstraint
146 {
147   CurrentCenterConstraint( float pageWidth )
148   : mPageWidth( pageWidth )
149   {
150     mThres = pageWidth * PAGE_TURN_OVER_THRESHOLD_RATIO * 0.5f;
151   }
152
153   void operator()( Vector2& current, const PropertyInputContainer& inputs )
154   {
155     const Vector2& centerPosition = inputs[0]->GetVector2();
156     if( centerPosition.x > 0.f )
157     {
158       current.x = mThres+centerPosition.x * 0.5f;
159       current.y = centerPosition.y;
160     }
161     else
162     {
163       const Vector2& centerOrigin = inputs[1]->GetVector2();
164       Vector2 direction = centerOrigin - Vector2(mThres, centerPosition.y);
165       float coef = 1.f+(centerPosition.x*2.f / mPageWidth);
166       // Todo: when coef <= 0, the page is flat, slow down the last moment of the page stretch by 10 times to avoid a small bounce
167       if(coef < 0.025f)
168       {
169         coef = (coef+0.225f)/10.0f;
170       }
171       current = centerOrigin - direction * coef;
172     }
173   }
174
175   float mPageWidth;
176   float mThres;
177 };
178
179 struct ShadowBlurStrengthConstraint
180 {
181   ShadowBlurStrengthConstraint( float thres )
182   : mThres( thres )
183   {}
184
185   void operator()( float& blurStrength,  const PropertyInputContainer& inputs )
186   {
187     float displacement = inputs[2]->GetFloat();
188     if( EqualsZero(displacement))
189     {
190       const Vector2& cur = inputs[0]->GetVector2();
191       const Vector2& ori = inputs[1]->GetVector2();
192       blurStrength =  5.f*(ori-cur).Length() / mThres;
193     }
194     else
195     {
196       blurStrength =  1.f - (displacement-2.f*mThres)/mThres;
197     }
198
199     blurStrength = blurStrength > 1.f ? 1.f : ( blurStrength < 0.f ? 0.f : blurStrength );
200   }
201
202   float mThres;
203 };
204
205 } //unnamed namespace
206
207 namespace Dali
208 {
209
210 namespace Toolkit
211 {
212
213 namespace Internal
214 {
215
216 namespace
217 {
218
219 BaseHandle Create()
220 {
221   // empty handle as we cannot create PageTurnView(but type registered for page turn signal)
222   return BaseHandle();
223 }
224
225 // Setup properties, signals and actions using the type-registry.
226 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::PageTurnView, Toolkit::Control, Create );
227
228 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "page-size",       VECTOR2, PAGE_SIZE )
229 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "current-page-id", INTEGER, CURRENT_PAGE_ID )
230 DALI_PROPERTY_REGISTRATION( Toolkit, PageTurnView, "spine-shadow",    VECTOR2, SPINE_SHADOW )
231
232 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-turn-started",  SIGNAL_PAGE_TURN_STARTED )
233 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-turn-finished", SIGNAL_PAGE_TURN_FINISHED )
234 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-pan-started",   SIGNAL_PAGE_PAN_STARTED )
235 DALI_SIGNAL_REGISTRATION( Toolkit, PageTurnView, "page-pan-finished",  SIGNAL_PAGE_PAN_FINISHED )
236
237 DALI_TYPE_REGISTRATION_END()
238
239 }
240
241 // these several constants are also used in the derived classes
242 const int PageTurnView::MAXIMUM_TURNING_NUM = 4;
243 const int PageTurnView::NUMBER_OF_CACHED_PAGES_EACH_SIDE = MAXIMUM_TURNING_NUM + 1;
244 const int PageTurnView::NUMBER_OF_CACHED_PAGES = NUMBER_OF_CACHED_PAGES_EACH_SIDE*2;
245 const float PageTurnView::STATIC_PAGE_INTERVAL_DISTANCE = 1.0f;
246
247 PageTurnView::PageTurnView( PageFactory& pageFactory, const Vector2& pageSize )
248 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
249   mPageFactory( pageFactory ),
250   mPageSize( pageSize ),
251   mTotalPageCount( 0 ),
252   mPanning( false ),
253   mSpineShadowParameter( DEFAULT_SPINE_SHADOW_PARAMETER ),
254   mCurrentPageIndex( 0 ),
255   mTurningPageIndex( 0 ),
256   mIndex( 0 ),
257   mPress( false ),
258   mPageUpdated( true ),
259   mDistanceUpCorner( 0.f ),
260   mDistanceBottomCorner( 0.f ),
261   mPanDisplacement( 0.f ),
262   mConstraints( false ),
263   mPageTurnStartedSignal(),
264   mPageTurnFinishedSignal(),
265   mPagePanStartedSignal(),
266   mPagePanFinishedSignal()
267 {
268   mPageActors.resize( NUMBER_OF_CACHED_PAGES );
269   mIsAnimating.resize( MAXIMUM_TURNING_NUM );
270   mIsSliding.resize( MAXIMUM_TURNING_NUM );
271   mTurnEffect.resize( MAXIMUM_TURNING_NUM );
272   mPropertyPanDisplacement.resize( MAXIMUM_TURNING_NUM );
273   mPropertyCurrentCenter.resize( MAXIMUM_TURNING_NUM );
274 }
275
276 PageTurnView::~PageTurnView()
277 {
278 }
279
280 void PageTurnView::OnInitialize()
281 {
282    // create the two book spine effect for static images, left and right side pages respectively
283   mSpineEffectFront = CreatePageTurnBookSpineEffect();
284   mSpineEffectFront.SetUniform("uIsBackImageVisible", -1.f );
285   mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
286   mSpineEffectFront.SetUniform("uShadowWidth", 0.f );
287   mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
288
289   mSpineEffectBack = CreatePageTurnBookSpineEffect();
290   mSpineEffectBack.SetUniform("uIsBackImageVisible", 1.f );
291   mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
292   mSpineEffectBack.SetUniform("uShadowWidth", 0.f );
293   mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
294
295   // create the page turn effect objects
296   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
297   {
298     mTurnEffect[i] = CreatePageTurnEffect();
299     mTurnEffect[i].SetProperty( ShaderEffect::Property::GRID_DENSITY, Property::Value( DEFAULT_GRID_DENSITY ) );
300     mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
301     mTurnEffect[i].SetUniform( "uShadowWidth", 0.f);
302     mTurnEffect[i].SetUniform( "uSpineShadowParameter", mSpineShadowParameter );
303     mIsAnimating[i] = false;
304     mIsSliding[i] = false;
305     mPropertyPanDisplacement[i] = Self().RegisterProperty("PAN_DISPLACEMENT_PROPERTY_"+i, 0.0f);
306     mPropertyCurrentCenter[i] = Self().RegisterProperty("CURRENT_CENTER_PROPERTY_"+i, Vector2(0.0f,0.0f));
307   }
308
309   mTurningPageLayer = Layer::New();
310   mTurningPageLayer.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
311   mTurningPageLayer.SetBehavior(Layer::LAYER_3D);
312
313   // Set control size and the parent origin of page layers
314   OnPageTurnViewInitialize();
315
316   Self().Add(mTurningPageLayer);
317
318   mTotalPageCount = static_cast<int>( mPageFactory.GetNumberOfPages() );
319   // add pages to the scene, and set depth for the stacked pages
320   for( int i = 0; i < NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
321   {
322     AddPage( i );
323     if(mPageActors[i])
324     {
325       mPageActors[i].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
326     }
327   }
328   mPageActors[0].SetVisible(true);
329
330   // enable the pan gesture which is attached to the control
331   EnableGestureDetection(Gesture::Type(Gesture::Pan));
332 }
333
334 void PageTurnView::SetupShadowView()
335 {
336   mShadowView = Toolkit::ShadowView::New( 0.25f, 0.25f );
337   Vector3 origin = mTurningPageLayer.GetCurrentParentOrigin();
338   mShadowView.SetParentOrigin( origin );
339   mShadowView.SetAnchorPoint( origin );
340   mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
341   mShadowView.SetShadowColor(DEFAULT_SHADOW_COLOR);
342
343   mShadowPlane = CreateSolidColorActor( Vector4 (0.0f, 0.0f, 0.0f, 0.0f) );
344   mShadowPlane.SetPositionInheritanceMode( USE_PARENT_POSITION_PLUS_LOCAL_POSITION );
345   mShadowPlane.SetSize( mControlSize );
346   Self().Add( mShadowPlane );
347   mShadowView.SetShadowPlane( mShadowPlane );
348
349   mPointLight = Actor::New();
350   mPointLight.SetAnchorPoint( origin );
351   mPointLight.SetParentOrigin( origin );
352   mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
353   Self().Add( mPointLight );
354   mShadowView.SetPointLight( mPointLight );
355
356   mTurningPageLayer.Add( mShadowView );
357   mShadowView.Activate();
358 }
359
360 void PageTurnView::OnStageConnection( int depth )
361 {
362   SetupShadowView();
363   mTurningPageLayer.Raise();
364 }
365
366 void PageTurnView::OnStageDisconnection()
367 {
368   if(mShadowView)
369   {
370     mPointLight.Unparent();
371     mShadowPlane.Unparent();
372     mShadowView.Unparent();
373   }
374
375   // make sure the status of the control is updated correctly when the pan gesture is interrupted
376   if(mPanning)
377   {
378     mPanning = false;
379
380     Self().Add(mPanActor);
381     mIsAnimating[mIndex] = false;
382     mPanActor.RemoveConstraints();
383     mTurnEffect[mIndex].RemoveConstraints();
384     mPageUpdated = true;
385
386     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
387   }
388 }
389
390 void PageTurnView::SetPageSize( const Vector2& pageSize )
391 {
392   mPageSize = pageSize;
393   mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
394   mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
395   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
396   {
397     mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
398   }
399
400   if( mPointLight )
401   {
402     mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
403   }
404
405   for( size_t i=0; i<mPageActors.size(); i++ )
406   {
407     if( mPageActors[i] )
408     {
409       mPageActors[i].SetSize( mPageSize );
410       if( mPageActors[i].GetChildCount()>0 )
411       {
412         mPageActors[i].GetChildAt(0).SetSize( mPageSize );
413       }
414     }
415   }
416
417   OnPageTurnViewInitialize();
418
419   if( mShadowPlane )
420   {
421     mShadowPlane.SetSize( mControlSize );
422   }
423 }
424
425 Vector2 PageTurnView::GetPageSize()
426 {
427   return mPageSize;
428 }
429
430 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
431 {
432   mSpineShadowParameter = spineShadowParameter;
433
434   // set spine shadow parameter to all the shader effects
435   mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
436   mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
437   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
438   {
439     mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
440   }
441 }
442
443 Vector2 PageTurnView::GetSpineShadowParameter()
444 {
445   return mSpineShadowParameter;
446 }
447
448 void PageTurnView::GoToPage( unsigned int pageId )
449 {
450   int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
451
452   if( mCurrentPageIndex == pageIdx  )
453   {
454     return;
455   }
456
457   // record the new current page index
458   mCurrentPageIndex = pageIdx;
459
460   // clear the old pages
461   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
462   {
463     if( mPageActors[i] )
464     {
465       mPageActors[i].Unparent();
466       mPageActors[i].Reset();
467     }
468   }
469
470   // add the current page and the pages right before and after it
471   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
472   {
473     AddPage( i );
474   }
475
476   mPageActors[pageId%NUMBER_OF_CACHED_PAGES].SetVisible(true);
477   if( pageId > 0 )
478   {
479     mPageActors[(pageId-1)%NUMBER_OF_CACHED_PAGES].SetVisible(true);
480   }
481   // set ordered depth to the stacked pages
482   OrganizePageDepth();
483 }
484
485 unsigned int PageTurnView::GetCurrentPage()
486 {
487   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
488   return static_cast< unsigned int >( mCurrentPageIndex );
489 }
490
491 void PageTurnView::AddPage( int pageIndex )
492 {
493   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
494   {
495     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
496     ImageActor newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
497      DALI_ASSERT_ALWAYS( newPage );
498
499     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
500     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
501     newPage.SetSize( mPageSize );
502     Self().Add( newPage );
503     mPageActors[index] = newPage;
504
505     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
506     mIsTurnBack[ newPage ] = isLeftSide;
507     if( isLeftSide )
508     {
509       // new page is added to the left side, so need to rotate it 180 degrees
510       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
511     }
512     else
513     {
514       newPage.SetShaderEffect(mSpineEffectFront);
515     }
516
517     newPage.SetVisible( false );
518
519     // For Portrait, nothing to do
520     // For Landscape, set spineEffectBack to the new effect if it is in the left side, and set properties to the back image actor if it exists
521     OnAddPage( newPage, isLeftSide );
522   }
523 }
524
525 void PageTurnView::RemovePage( int pageIndex )
526 {
527   if( pageIndex > -1 && pageIndex < mTotalPageCount)
528   {
529     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
530     mPageActors[index].Unparent();
531     mIsTurnBack.erase( mPageActors[index] );
532     mPageActors[index].Reset();
533   }
534 }
535
536 void PageTurnView::OnPan( const PanGesture& gesture )
537 {
538   // the pan gesture is attached to control itself instead of each page
539   switch( gesture.state )
540   {
541     case Gesture::Started:
542     {
543       mPanning = true;
544       // to find out whether the undergoing turning page number already reaches the maximum allowed
545       // and get one idle index when it is animatable
546       bool animatable = false;
547       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
548       {
549         if( !mIsAnimating[mIndex] )
550         {
551           animatable = true;
552           break;
553         }
554         if( mIsSliding[mIndex] )
555         {
556           animatable = false;
557           break;
558         }
559         mIndex++;
560         mIndex = mIndex % MAXIMUM_TURNING_NUM;
561       }
562
563       if( mPageUpdated && animatable )
564       {
565         SetPanActor( gesture.position ); // determine which page actor is panned
566         if(mPanActor && mPanActor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
567         {
568           mPanActor.Reset();
569         }
570         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
571       }
572       else
573       {
574         mPanActor.Reset();
575       }
576       break;
577     }
578     case Gesture::Continuing:
579     {
580       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
581       break;
582     }
583     case Gesture::Finished:
584     case Gesture::Cancelled:
585     {
586       mPanning = false;
587       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
588       break;
589     }
590     case Gesture::Clear:
591     case Gesture::Possible:
592     default:
593     {
594       break;
595     }
596   }
597 }
598
599 void PageTurnView::PanStarted( const Vector2& gesturePosition )
600 {
601   mPressDownPosition = gesturePosition;
602
603   if( !mPanActor )
604   {
605     return;
606   }
607
608   mOriginalCenter = gesturePosition;
609   mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
610   mPress = false;
611   mPageUpdated = false;
612
613   // Guard against destruction during signal emission
614   Toolkit::PageTurnView handle( GetOwner() );
615   mPagePanStartedSignal.Emit( handle );
616 }
617
618 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
619 {
620   if( !mPanActor )
621   {
622     return;
623   }
624
625   // Guard against destruction during signal emission
626   Toolkit::PageTurnView handle( GetOwner() );
627
628   if(!mPress)
629   {
630     // when the touch down position is near the spine
631     // or when the panning goes outwards or some other position which would tear the paper in real situation
632     // we change the start position into the current panning position and update the shader parameters
633     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
634         || gesturePosition.x > mOriginalCenter.x-1.0f
635         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
636              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
637     {
638       mOriginalCenter = gesturePosition;
639     }
640     else
641     {
642       mDistanceUpCorner = mOriginalCenter.Length();
643       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
644       mShadowView.Add( mPanActor );
645       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
646       mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
647       mCurrentCenter = mOriginalCenter;
648       mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
649       mPanDisplacement = 0.f;
650       mConstraints = true;
651       mPress = true;
652       mIsAnimating[mIndex] = true;
653
654       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mIsTurnBack[mPanActor] );
655       int id = mTurningPageIndex + (mIsTurnBack[mPanActor]? -1 : 1);
656       if( id >=0 && id < mTotalPageCount )
657       {
658         mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(true);
659       }
660
661       mShadowView.RemoveConstraints();
662       Actor self = Self();
663       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
664
665       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
666       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
667       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
668       shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
669       shadowBlurStrengthConstraint.Apply();
670     }
671   }
672   else
673   {
674     Vector2 currentCenter = gesturePosition;
675
676     // Test whether the new current center would tear the paper from the top pine in real situation
677     // we do not forbid this totally, which would restrict the panning gesture too much
678     // instead, set it to the nearest allowable position
679     float distanceUpCorner = currentCenter.Length();
680     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
681     if( distanceUpCorner > mDistanceUpCorner )
682     {
683       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
684     }
685     // would tear the paper from the bottom spine in real situation
686     if( distanceBottomCorner > mDistanceBottomCorner )
687     {
688       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
689     }
690     // If direction has a very high y component, reduce it.
691     Vector2 curveDirection = currentCenter - mOriginalCenter;
692     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
693     {
694       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
695     }
696     // If the vertical distance is high, reduce it
697     float yShift = currentCenter.y - mOriginalCenter.y;
698     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
699     {
700       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
701     }
702
703     // use contraints to control the page shape and rotation when the pan position is near the spine
704     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
705     {
706       // set the property values used by the constraints
707       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
708       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
709       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
710
711       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
712       // also set up the RotationConstraint to the page actor
713       if( mConstraints )
714       {
715         Vector2 corner;
716         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
717         if( currentCenter.y >= mOriginalCenter.y )
718         {
719           corner = Vector2( 1.1f*mPageSize.width, 0.f );
720         }
721         else
722         {
723           corner = mPageSize*1.1f;
724         }
725
726         Vector2 offset( currentCenter-mOriginalCenter );
727         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
728                    /( offset.x*offset.x + offset.y*offset.y );
729         offset *= k;
730         Actor self = Self();
731
732         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
733         Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
734         originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
735         originalCenterConstraint.Apply();
736
737         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
738         Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
739         currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
740         currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
741         currentCenterConstraint.Apply();
742
743         PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
744
745         float distance = offset.Length();
746         Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
747         rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
748         rotationConstraint.Apply();
749
750         mConstraints = false;
751       }
752     }
753     else
754     {
755       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
756       {
757         mPanActor.RemoveConstraints();
758         mTurnEffect[mIndex].RemoveConstraints();
759         mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
760         mConstraints = true;
761         mPanDisplacement = 0.f;
762       }
763
764       mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
765       mCurrentCenter = currentCenter;
766       PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
767     }
768   }
769 }
770
771 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
772 {
773   // Guard against destruction during signal emission
774   Toolkit::PageTurnView handle( GetOwner() );
775
776   if( !mPanActor )
777   {
778     if(!mIsAnimating[mIndex])
779     {
780       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
781     }
782     return;
783   }
784
785   mPagePanFinishedSignal.Emit( handle );
786
787   ImageActor actor = mPanActor;
788   if(mPress)
789   {
790     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
791     {
792       // update the pages here instead of in the TurnedOver callback function
793       // as new page is allowed to respond to the pan gesture before other pages finishing animation
794       if(mIsTurnBack[actor])
795       {
796         mCurrentPageIndex--;
797         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
798         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
799       }
800       else
801       {
802         mCurrentPageIndex++;
803         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
804         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
805       }
806       OrganizePageDepth();
807
808       // set up an animation to turn the page over
809       Actor self = Self();
810       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
811       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
812       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
813                            width,AlphaFunction::EASE_OUT_SINE);
814       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
815                            Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
816       mAnimationPageIdPair[animation] = mTurningPageIndex;
817       mAnimationIndexPair[animation] = mIndex;
818       animation.Play();
819       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
820     }
821     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
822     {
823       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
824       animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
825                            mOriginalCenter, AlphaFunction::LINEAR );
826       mAnimationPageIdPair[animation] = mTurningPageIndex;
827       mAnimationIndexPair[animation] = mIndex;
828       animation.Play();
829       mIsSliding[mIndex] = true;
830       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
831
832       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mIsTurnBack[actor] );
833     }
834   }
835   else
836   {
837     // In portrait view, an outwards flick should turn the previous page back
838     // In landscape view, nothing to do
839     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
840   }
841
842   mPageUpdated = true;
843 }
844
845 void PageTurnView::TurnedOver( Animation& animation )
846 {
847   int pageId = mAnimationPageIdPair[animation];
848   ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
849   mIsTurnBack[actor] = !mIsTurnBack[actor];
850   actor.RemoveConstraints();
851   Self().Add(actor);
852   int index = mAnimationIndexPair[animation];
853   mIsAnimating[index] = false;
854   mTurnEffect[index].RemoveConstraints();
855   mAnimationIndexPair.erase( animation );
856   mAnimationPageIdPair.erase( animation );
857
858   SetSpineEffect( actor, mIsTurnBack[actor] );
859
860   int id = pageId + (mIsTurnBack[actor]? -1 : 1);
861   if( id >=0 && id < mTotalPageCount )
862   {
863     mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
864   }
865
866   OnTurnedOver( actor, mIsTurnBack[actor] );
867
868   // Guard against destruction during signal emission
869   Toolkit::PageTurnView handle( GetOwner() );
870   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
871 }
872
873 void PageTurnView::SliddenBack( Animation& animation )
874 {
875   int pageId = mAnimationPageIdPair[animation];
876   ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
877   Self().Add(actor);
878   int index = mAnimationIndexPair[animation];
879   mIsSliding[index] = false;
880   mIsAnimating[index] = false;
881   mAnimationIndexPair.erase( animation );
882   mAnimationPageIdPair.erase( animation );
883
884   SetSpineEffect( actor, mIsTurnBack[actor] );
885
886   int id = pageId + (mIsTurnBack[actor]? -1 : 1);
887   if( id >=0 && id < mTotalPageCount )
888   {
889     mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
890   }
891
892   // Guard against destruction during signal emission
893   Toolkit::PageTurnView handle( GetOwner() );
894   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
895 }
896
897 void PageTurnView::OrganizePageDepth()
898 {
899   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
900   {
901     if(mCurrentPageIndex+i < mTotalPageCount)
902     {
903       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
904     }
905     if( mCurrentPageIndex >= i + 1 )
906     {
907       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
908     }
909   }
910 }
911
912 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
913 {
914   SetShaderEffectRecursively( actor, shaderEffect );
915 }
916
917 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
918 {
919   return mPageTurnStartedSignal;
920 }
921
922 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
923 {
924   return mPageTurnFinishedSignal;
925 }
926
927 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
928 {
929   return mPagePanStartedSignal;
930 }
931
932 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
933 {
934   return mPagePanFinishedSignal;
935 }
936
937 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
938 {
939   Dali::BaseHandle handle( object );
940
941   bool connected( true );
942   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
943
944   if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
945   {
946     pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
947   }
948   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
949   {
950     pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
951   }
952   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
953   {
954     pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
955   }
956   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
957   {
958     pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
959   }
960   else
961   {
962     // signalName does not match any signal
963     connected = false;
964   }
965
966   return connected;
967 }
968
969 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
970 {
971   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
972
973   if( pageTurnView )
974   {
975     PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
976
977     switch( index )
978     {
979       case Toolkit::PageTurnView::Property::PAGE_SIZE:
980       {
981         pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
982         break;
983       }
984       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
985       {
986         pageTurnViewImpl.GoToPage( value.Get<int>() );
987         break;
988       }
989       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
990       {
991         pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
992         break;
993       }
994     }
995   }
996 }
997
998 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
999 {
1000   Property::Value value;
1001
1002   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1003
1004   if( pageTurnView )
1005   {
1006     PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1007
1008     switch( index )
1009     {
1010       case Toolkit::PageTurnView::Property::PAGE_SIZE:
1011       {
1012         value = pageTurnViewImpl.GetPageSize();
1013         break;
1014       }
1015       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1016       {
1017         value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1018         break;
1019       }
1020       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1021       {
1022         value = pageTurnViewImpl.GetSpineShadowParameter();
1023         break;
1024       }
1025     }
1026   }
1027   return value;
1028 }
1029
1030 } // namespace Internal
1031
1032 } // namespace Toolkit
1033
1034 } // namespace Dali