[dali_1.1.8] 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/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   Control::OnStageConnection( depth );
362
363   SetupShadowView();
364   mTurningPageLayer.Raise();
365 }
366
367 void PageTurnView::OnStageDisconnection()
368 {
369   if(mShadowView)
370   {
371     mPointLight.Unparent();
372     mShadowPlaneBackground.Unparent();
373     mShadowView.Unparent();
374   }
375
376   // make sure the status of the control is updated correctly when the pan gesture is interrupted
377   if(mPanning)
378   {
379     mPanning = false;
380
381     Self().Add(mPanActor);
382     mIsAnimating[mIndex] = false;
383     mPanActor.RemoveConstraints();
384     mTurnEffect[mIndex].RemoveConstraints();
385     mPageUpdated = true;
386
387     SetSpineEffect( mPanActor, mIsTurnBack[mPanActor] );
388   }
389
390   Control::OnStageDisconnection();
391 }
392
393 void PageTurnView::SetPageSize( const Vector2& pageSize )
394 {
395   mPageSize = pageSize;
396   mSpineEffectFront.SetUniform("uPageWidth", mPageSize.width );
397   mSpineEffectBack.SetUniform("uPageWidth", mPageSize.width );
398   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
399   {
400     mTurnEffect[i].SetUniform( "uPageSize", mPageSize );
401   }
402
403   if( mPointLight )
404   {
405     mPointLight.SetPosition( 0.f, 0.f, mPageSize.width*POINT_LIGHT_HEIGHT_RATIO );
406   }
407
408   for( size_t i=0; i<mPageActors.size(); i++ )
409   {
410     if( mPageActors[i] )
411     {
412       mPageActors[i].SetSize( mPageSize );
413       if( mPageActors[i].GetChildCount()>0 )
414       {
415         mPageActors[i].GetChildAt(0).SetSize( mPageSize );
416       }
417     }
418   }
419
420   OnPageTurnViewInitialize();
421
422   if( mShadowPlaneBackground )
423   {
424     mShadowPlaneBackground.SetSize( mControlSize );
425   }
426 }
427
428 Vector2 PageTurnView::GetPageSize()
429 {
430   return mPageSize;
431 }
432
433 void PageTurnView::SetSpineShadowParameter( const Vector2& spineShadowParameter )
434 {
435   mSpineShadowParameter = spineShadowParameter;
436
437   // set spine shadow parameter to all the shader effects
438   mSpineEffectFront.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
439   mSpineEffectBack.SetUniform("uSpineShadowParameter", mSpineShadowParameter );
440   for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
441   {
442     mTurnEffect[i].SetUniform("uSpineShadowParameter", mSpineShadowParameter );
443   }
444 }
445
446 Vector2 PageTurnView::GetSpineShadowParameter()
447 {
448   return mSpineShadowParameter;
449 }
450
451 void PageTurnView::GoToPage( unsigned int pageId )
452 {
453   int pageIdx = Clamp( static_cast<int>(pageId), 0, mTotalPageCount-1);
454
455   if( mCurrentPageIndex == pageIdx  )
456   {
457     return;
458   }
459
460   // record the new current page index
461   mCurrentPageIndex = pageIdx;
462
463   // clear the old pages
464   for(int i = 0; i < NUMBER_OF_CACHED_PAGES; i++ )
465   {
466     if( mPageActors[i] )
467     {
468       mPageActors[i].Unparent();
469       mPageActors[i].Reset();
470     }
471   }
472
473   // add the current page and the pages right before and after it
474   for( int i = pageIdx - NUMBER_OF_CACHED_PAGES_EACH_SIDE; i < pageIdx + NUMBER_OF_CACHED_PAGES_EACH_SIDE; i++ )
475   {
476     AddPage( i );
477   }
478
479   mPageActors[pageId%NUMBER_OF_CACHED_PAGES].SetVisible(true);
480   if( pageId > 0 )
481   {
482     mPageActors[(pageId-1)%NUMBER_OF_CACHED_PAGES].SetVisible(true);
483   }
484   // set ordered depth to the stacked pages
485   OrganizePageDepth();
486 }
487
488 unsigned int PageTurnView::GetCurrentPage()
489 {
490   DALI_ASSERT_ALWAYS( mCurrentPageIndex >= 0 );
491   return static_cast< unsigned int >( mCurrentPageIndex );
492 }
493
494 void PageTurnView::AddPage( int pageIndex )
495 {
496   if(pageIndex > -1  && pageIndex < mTotalPageCount) // whether the page is available from the page factory
497   {
498     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
499     ImageActor newPage= ImageActor::DownCast( mPageFactory.NewPage( pageIndex ) );
500      DALI_ASSERT_ALWAYS( newPage );
501
502     newPage.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
503     newPage.SetParentOrigin( ParentOrigin::CENTER_LEFT );
504     newPage.SetSize( mPageSize );
505     Self().Add( newPage );
506     mPageActors[index] = newPage;
507
508     bool isLeftSide = ( pageIndex < mCurrentPageIndex );
509     mIsTurnBack[ newPage ] = isLeftSide;
510     if( isLeftSide )
511     {
512       // new page is added to the left side, so need to rotate it 180 degrees
513       newPage.RotateBy( Degree(-180.0f ), Vector3::YAXIS );
514     }
515     else
516     {
517       newPage.SetShaderEffect(mSpineEffectFront);
518     }
519
520     newPage.SetVisible( false );
521
522     // For Portrait, nothing to do
523     // 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
524     OnAddPage( newPage, isLeftSide );
525   }
526 }
527
528 void PageTurnView::RemovePage( int pageIndex )
529 {
530   if( pageIndex > -1 && pageIndex < mTotalPageCount)
531   {
532     int index = pageIndex % NUMBER_OF_CACHED_PAGES;
533     mPageActors[index].Unparent();
534     mIsTurnBack.erase( mPageActors[index] );
535     mPageActors[index].Reset();
536   }
537 }
538
539 void PageTurnView::OnPan( const PanGesture& gesture )
540 {
541   // the pan gesture is attached to control itself instead of each page
542   switch( gesture.state )
543   {
544     case Gesture::Started:
545     {
546       mPanning = true;
547       // to find out whether the undergoing turning page number already reaches the maximum allowed
548       // and get one idle index when it is animatable
549       bool animatable = false;
550       for( int i = 0; i < MAXIMUM_TURNING_NUM; i++ )
551       {
552         if( !mIsAnimating[mIndex] )
553         {
554           animatable = true;
555           break;
556         }
557         if( mIsSliding[mIndex] )
558         {
559           animatable = false;
560           break;
561         }
562         mIndex++;
563         mIndex = mIndex % MAXIMUM_TURNING_NUM;
564       }
565
566       if( mPageUpdated && animatable )
567       {
568         SetPanActor( gesture.position ); // determine which page actor is panned
569         if(mPanActor && mPanActor.GetParent() != Self()) // if the page is added to turning layer,it is undergoing an animation currently
570         {
571           mPanActor.Reset();
572         }
573         PanStarted( SetPanPosition( gesture.position ) );  // pass in the pan position in the local page coordinate
574       }
575       else
576       {
577         mPanActor.Reset();
578       }
579       break;
580     }
581     case Gesture::Continuing:
582     {
583       PanContinuing( SetPanPosition( gesture.position ) ); // pass in the pan position in the local page coordinate
584       break;
585     }
586     case Gesture::Finished:
587     case Gesture::Cancelled:
588     {
589       mPanning = false;
590       PanFinished( SetPanPosition( gesture.position ), gesture.GetSpeed() );
591       break;
592     }
593     case Gesture::Clear:
594     case Gesture::Possible:
595     default:
596     {
597       break;
598     }
599   }
600 }
601
602 void PageTurnView::PanStarted( const Vector2& gesturePosition )
603 {
604   mPressDownPosition = gesturePosition;
605
606   if( !mPanActor )
607   {
608     return;
609   }
610
611   mOriginalCenter = gesturePosition;
612   mTurnEffect[mIndex].SetUniform("uIsTurningBack", mIsTurnBack[ mPanActor] ? 1.f : -1.f);
613   mPress = false;
614   mPageUpdated = false;
615
616   // Guard against destruction during signal emission
617   Toolkit::PageTurnView handle( GetOwner() );
618   mPagePanStartedSignal.Emit( handle );
619 }
620
621 void PageTurnView::PanContinuing( const Vector2& gesturePosition )
622 {
623   if( !mPanActor )
624   {
625     return;
626   }
627
628   // Guard against destruction during signal emission
629   Toolkit::PageTurnView handle( GetOwner() );
630
631   if(!mPress)
632   {
633     // when the touch down position is near the spine
634     // or when the panning goes outwards or some other position which would tear the paper in real situation
635     // we change the start position into the current panning position and update the shader parameters
636     if( mOriginalCenter.x <  mPageSize.width*MINIMUM_START_POSITION_RATIO
637         || gesturePosition.x > mOriginalCenter.x-1.0f
638         || ( ( gesturePosition.x/mOriginalCenter.x > gesturePosition.y/mOriginalCenter.y ) &&
639              ( gesturePosition.x/mOriginalCenter.x > (gesturePosition.y-mPageSize.height )/(mOriginalCenter.y-mPageSize.height ) ) ) )
640     {
641       mOriginalCenter = gesturePosition;
642     }
643     else
644     {
645       mDistanceUpCorner = mOriginalCenter.Length();
646       mDistanceBottomCorner = ( mOriginalCenter - Vector2( 0.0f, mPageSize.height ) ).Length();
647       mShadowView.Add( mPanActor );
648       SetShaderEffect( mPanActor, mTurnEffect[mIndex] );
649       mTurnEffect[mIndex].SetUniform("uOriginalCenter", mOriginalCenter );
650       mCurrentCenter = mOriginalCenter;
651       mTurnEffect[mIndex].SetUniform("uCurrentCenter", mCurrentCenter );
652       mPanDisplacement = 0.f;
653       mConstraints = true;
654       mPress = true;
655       mIsAnimating[mIndex] = true;
656
657       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), !mIsTurnBack[mPanActor] );
658       int id = mTurningPageIndex + (mIsTurnBack[mPanActor]? -1 : 1);
659       if( id >=0 && id < mTotalPageCount )
660       {
661         mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(true);
662       }
663
664       mShadowView.RemoveConstraints();
665       Actor self = Self();
666       self.SetProperty( mPropertyPanDisplacement[mIndex], 0.f );
667
668       Constraint shadowBlurStrengthConstraint = Constraint::New<float>( mShadowView, mShadowView.GetBlurStrengthPropertyIndex(), ShadowBlurStrengthConstraint( mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO ) );
669       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter")) );
670       shadowBlurStrengthConstraint.AddSource( Source(mTurnEffect[mIndex],  mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter")) );
671       shadowBlurStrengthConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
672       shadowBlurStrengthConstraint.Apply();
673     }
674   }
675   else
676   {
677     Vector2 currentCenter = gesturePosition;
678
679     // Test whether the new current center would tear the paper from the top pine in real situation
680     // we do not forbid this totally, which would restrict the panning gesture too much
681     // instead, set it to the nearest allowable position
682     float distanceUpCorner = currentCenter.Length();
683     float distanceBottomCorner = ( currentCenter-Vector2( 0.0f, mPageSize.height ) ).Length();
684     if( distanceUpCorner > mDistanceUpCorner )
685     {
686       currentCenter = currentCenter*mDistanceUpCorner/distanceUpCorner;
687     }
688     // would tear the paper from the bottom spine in real situation
689     if( distanceBottomCorner > mDistanceBottomCorner )
690     {
691       currentCenter = ( ( currentCenter - Vector2( 0.0f, mPageSize.height ) )*mDistanceBottomCorner/distanceBottomCorner + Vector2(0.0f,mPageSize.height ) );
692     }
693     // If direction has a very high y component, reduce it.
694     Vector2 curveDirection = currentCenter - mOriginalCenter;
695     if( fabs( curveDirection.y ) > fabs( curveDirection.x ) )
696     {
697       currentCenter.y = mOriginalCenter.y + ( currentCenter.y - mOriginalCenter.y ) * fabs( curveDirection.x/curveDirection.y );
698     }
699     // If the vertical distance is high, reduce it
700     float yShift = currentCenter.y - mOriginalCenter.y;
701     if( fabs( yShift ) > mPageSize.height * MAXIMUM_VERTICAL_MOVEMENT_RATIO )
702     {
703       currentCenter.y = mOriginalCenter.y + yShift*mPageSize.height*MAXIMUM_VERTICAL_MOVEMENT_RATIO/fabs(yShift );
704     }
705
706     // use contraints to control the page shape and rotation when the pan position is near the spine
707     if( currentCenter.x <= mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO && mOriginalCenter.x > mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO )
708     {
709       // set the property values used by the constraints
710       mPanDisplacement = mPageSize.width*PAGE_TURN_OVER_THRESHOLD_RATIO - currentCenter.x;
711       Self().SetProperty( mPropertyPanDisplacement[mIndex], mPanDisplacement );
712       Self().SetProperty( mPropertyCurrentCenter[mIndex], currentCenter );
713
714       // set up the OriginalCenterConstraint and CurrentCebterConstraint to the PageTurnEdffect
715       // also set up the RotationConstraint to the page actor
716       if( mConstraints )
717       {
718         Vector2 corner;
719         // the corner position need to be a little far away from the page edge to ensure the whole page is lift up
720         if( currentCenter.y >= mOriginalCenter.y )
721         {
722           corner = Vector2( 1.1f*mPageSize.width, 0.f );
723         }
724         else
725         {
726           corner = mPageSize*1.1f;
727         }
728
729         Vector2 offset( currentCenter-mOriginalCenter );
730         float k = - ( (mOriginalCenter.x-corner.x)*offset.x + (mOriginalCenter.y-corner.y)*offset.y )
731                    /( offset.x*offset.x + offset.y*offset.y );
732         offset *= k;
733         Actor self = Self();
734
735         Property::Index shaderOriginalCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uOriginalCenter");
736         Constraint originalCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex, OriginalCenterConstraint( mOriginalCenter, offset ));
737         originalCenterConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
738         originalCenterConstraint.Apply();
739
740         Property::Index shaderCurrentCenterPropertyIndex = mTurnEffect[mIndex].GetPropertyIndex("uCurrentCenter");
741         Constraint currentCenterConstraint = Constraint::New<Vector2>( mTurnEffect[mIndex], shaderCurrentCenterPropertyIndex, CurrentCenterConstraint(mPageSize.width));
742         currentCenterConstraint.AddSource( Source(self, mPropertyCurrentCenter[mIndex]) );
743         currentCenterConstraint.AddSource( Source(mTurnEffect[mIndex], shaderOriginalCenterPropertyIndex) );
744         currentCenterConstraint.Apply();
745
746         PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
747
748         float distance = offset.Length();
749         Constraint rotationConstraint = Constraint::New<Quaternion>( mPanActor, Actor::Property::ORIENTATION, RotationConstraint(distance, mPageSize.width, mIsTurnBack[mPanActor]));
750         rotationConstraint.AddSource( Source( self, mPropertyPanDisplacement[mIndex] ) );
751         rotationConstraint.Apply();
752
753         mConstraints = false;
754       }
755     }
756     else
757     {
758       if(!mConstraints) // remove the constraint is the pan position move back to far away from the spine
759       {
760         mPanActor.RemoveConstraints();
761         mTurnEffect[mIndex].RemoveConstraints();
762         mTurnEffect[mIndex].SetUniform("uOriginalCenter",mOriginalCenter );
763         mConstraints = true;
764         mPanDisplacement = 0.f;
765       }
766
767       mTurnEffect[mIndex].SetUniform("uCurrentCenter", currentCenter );
768       mCurrentCenter = currentCenter;
769       PageTurnApplyInternalConstraint(mTurnEffect[mIndex]);
770     }
771   }
772 }
773
774 void PageTurnView::PanFinished( const Vector2& gesturePosition, float gestureSpeed )
775 {
776   // Guard against destruction during signal emission
777   Toolkit::PageTurnView handle( GetOwner() );
778
779   if( !mPanActor )
780   {
781     if(!mIsAnimating[mIndex])
782     {
783       OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
784     }
785     return;
786   }
787
788   mPagePanFinishedSignal.Emit( handle );
789
790   ImageActor actor = mPanActor;
791   if(mPress)
792   {
793     if(!mConstraints) // if with constraints, the pan finished position is near spine, set up an animation to turn the page over
794     {
795       // update the pages here instead of in the TurnedOver callback function
796       // as new page is allowed to respond to the pan gesture before other pages finishing animation
797       if(mIsTurnBack[actor])
798       {
799         mCurrentPageIndex--;
800         RemovePage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE );
801         AddPage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE );
802       }
803       else
804       {
805         mCurrentPageIndex++;
806         RemovePage( mCurrentPageIndex-NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
807         AddPage( mCurrentPageIndex+NUMBER_OF_CACHED_PAGES_EACH_SIDE-1 );
808       }
809       OrganizePageDepth();
810
811       // set up an animation to turn the page over
812       Actor self = Self();
813       float width = mPageSize.width*(1.f+PAGE_TURN_OVER_THRESHOLD_RATIO);
814       Animation animation = Animation::New( std::max(0.1f,PAGE_TURN_OVER_ANIMATION_DURATION * (1.0f - mPanDisplacement / width)) );
815       animation.AnimateTo( Property(self, mPropertyPanDisplacement[mIndex]),
816                            width,AlphaFunction::EASE_OUT_SINE);
817       animation.AnimateTo( Property(self, mPropertyCurrentCenter[mIndex]),
818                            Vector2(-mPageSize.width*1.1f, 0.5f*mPageSize.height), AlphaFunction::EASE_OUT_SINE);
819       mAnimationPageIdPair[animation] = mTurningPageIndex;
820       mAnimationIndexPair[animation] = mIndex;
821       animation.Play();
822       animation.FinishedSignal().Connect( this, &PageTurnView::TurnedOver );
823     }
824     else // the pan finished position is far away from the spine, set up an animation to slide the page back instead of turning over
825     {
826       Animation animation= Animation::New( PAGE_SLIDE_BACK_ANIMATION_DURATION * (mOriginalCenter.x - mCurrentCenter.x) / mPageSize.width / PAGE_TURN_OVER_THRESHOLD_RATIO );
827       animation.AnimateTo( Property( mTurnEffect[mIndex], "uCurrentCenter" ),
828                            mOriginalCenter, AlphaFunction::LINEAR );
829       mAnimationPageIdPair[animation] = mTurningPageIndex;
830       mAnimationIndexPair[animation] = mIndex;
831       animation.Play();
832       mIsSliding[mIndex] = true;
833       animation.FinishedSignal().Connect( this, &PageTurnView::SliddenBack );
834
835       mPageTurnStartedSignal.Emit( handle, static_cast<unsigned int>(mTurningPageIndex), mIsTurnBack[actor] );
836     }
837   }
838   else
839   {
840     // In portrait view, an outwards flick should turn the previous page back
841     // In landscape view, nothing to do
842     OnPossibleOutwardsFlick( gesturePosition, gestureSpeed );
843   }
844
845   mPageUpdated = true;
846 }
847
848 void PageTurnView::TurnedOver( Animation& animation )
849 {
850   int pageId = mAnimationPageIdPair[animation];
851   ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
852   mIsTurnBack[actor] = !mIsTurnBack[actor];
853   actor.RemoveConstraints();
854   Self().Add(actor);
855   int index = mAnimationIndexPair[animation];
856   mIsAnimating[index] = false;
857   mTurnEffect[index].RemoveConstraints();
858   mAnimationIndexPair.erase( animation );
859   mAnimationPageIdPair.erase( animation );
860
861   SetSpineEffect( actor, mIsTurnBack[actor] );
862
863   int id = pageId + (mIsTurnBack[actor]? -1 : 1);
864   if( id >=0 && id < mTotalPageCount )
865   {
866     mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
867   }
868
869   OnTurnedOver( actor, mIsTurnBack[actor] );
870
871   // Guard against destruction during signal emission
872   Toolkit::PageTurnView handle( GetOwner() );
873   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
874 }
875
876 void PageTurnView::SliddenBack( Animation& animation )
877 {
878   int pageId = mAnimationPageIdPair[animation];
879   ImageActor actor = mPageActors[pageId % NUMBER_OF_CACHED_PAGES];
880   Self().Add(actor);
881   int index = mAnimationIndexPair[animation];
882   mIsSliding[index] = false;
883   mIsAnimating[index] = false;
884   mAnimationIndexPair.erase( animation );
885   mAnimationPageIdPair.erase( animation );
886
887   SetSpineEffect( actor, mIsTurnBack[actor] );
888
889   int id = pageId + (mIsTurnBack[actor]? -1 : 1);
890   if( id >=0 && id < mTotalPageCount )
891   {
892     mPageActors[id%NUMBER_OF_CACHED_PAGES].SetVisible(false);
893   }
894
895   // Guard against destruction during signal emission
896   Toolkit::PageTurnView handle( GetOwner() );
897   mPageTurnFinishedSignal.Emit( handle, static_cast<unsigned int>(pageId), mIsTurnBack[actor] );
898 }
899
900 void PageTurnView::OrganizePageDepth()
901 {
902   for( int i=0; i<NUMBER_OF_CACHED_PAGES_EACH_SIDE;i++ )
903   {
904     if(mCurrentPageIndex+i < mTotalPageCount)
905     {
906       mPageActors[( mCurrentPageIndex+i )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
907     }
908     if( mCurrentPageIndex >= i + 1 )
909     {
910       mPageActors[( mCurrentPageIndex-i-1 )%NUMBER_OF_CACHED_PAGES].SetZ( -static_cast<float>( i )*STATIC_PAGE_INTERVAL_DISTANCE );
911     }
912   }
913 }
914
915 void PageTurnView::SetShaderEffect( ImageActor actor, ShaderEffect shaderEffect )
916 {
917   SetShaderEffectRecursively( actor, shaderEffect );
918 }
919
920 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnStartedSignal()
921 {
922   return mPageTurnStartedSignal;
923 }
924
925 Toolkit::PageTurnView::PageTurnSignal& PageTurnView::PageTurnFinishedSignal()
926 {
927   return mPageTurnFinishedSignal;
928 }
929
930 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanStartedSignal()
931 {
932   return mPagePanStartedSignal;
933 }
934
935 Toolkit::PageTurnView::PagePanSignal& PageTurnView::PagePanFinishedSignal()
936 {
937   return mPagePanFinishedSignal;
938 }
939
940 bool PageTurnView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
941 {
942   Dali::BaseHandle handle( object );
943
944   bool connected( true );
945   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( handle );
946
947   if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_STARTED ) )
948   {
949     pageTurnView.PageTurnStartedSignal().Connect( tracker, functor );
950   }
951   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_TURN_FINISHED ) )
952   {
953     pageTurnView.PageTurnFinishedSignal().Connect( tracker, functor );
954   }
955   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_STARTED ) )
956   {
957     pageTurnView.PagePanStartedSignal().Connect( tracker, functor );
958   }
959   else if( 0 == strcmp( signalName.c_str(), SIGNAL_PAGE_PAN_FINISHED ) )
960   {
961     pageTurnView.PagePanFinishedSignal().Connect( tracker, functor );
962   }
963   else
964   {
965     // signalName does not match any signal
966     connected = false;
967   }
968
969   return connected;
970 }
971
972 void PageTurnView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
973 {
974   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
975
976   if( pageTurnView )
977   {
978     PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
979
980     switch( index )
981     {
982       case Toolkit::PageTurnView::Property::PAGE_SIZE:
983       {
984         pageTurnViewImpl.SetPageSize( value.Get<Vector2>() );
985         break;
986       }
987       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
988       {
989         pageTurnViewImpl.GoToPage( value.Get<int>() );
990         break;
991       }
992       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
993       {
994         pageTurnViewImpl.SetSpineShadowParameter( value.Get<Vector2>() );
995         break;
996       }
997     }
998   }
999 }
1000
1001 Property::Value PageTurnView::GetProperty( BaseObject* object, Property::Index index )
1002 {
1003   Property::Value value;
1004
1005   Toolkit::PageTurnView pageTurnView = Toolkit::PageTurnView::DownCast( Dali::BaseHandle( object ) );
1006
1007   if( pageTurnView )
1008   {
1009     PageTurnView& pageTurnViewImpl( GetImplementation( pageTurnView ) );
1010
1011     switch( index )
1012     {
1013       case Toolkit::PageTurnView::Property::PAGE_SIZE:
1014       {
1015         value = pageTurnViewImpl.GetPageSize();
1016         break;
1017       }
1018       case Toolkit::PageTurnView::Property::CURRENT_PAGE_ID:
1019       {
1020         value = static_cast<int>( pageTurnViewImpl.GetCurrentPage() );
1021         break;
1022       }
1023       case Toolkit::PageTurnView::Property::SPINE_SHADOW:
1024       {
1025         value = pageTurnViewImpl.GetSpineShadowParameter();
1026         break;
1027       }
1028     }
1029   }
1030   return value;
1031 }
1032
1033 } // namespace Internal
1034
1035 } // namespace Toolkit
1036
1037 } // namespace Dali