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