Merge "Removed bogus consts and fixed out-of-line definitions." into tizen
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
1 /*
2  * Copyright (c) 2014 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/internal/event/events/hit-test-algorithm-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/system-overlay.h>
23 #include <dali/public-api/math/vector2.h>
24 #include <dali/public-api/math/vector4.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/internal/event/actors/actor-impl.h>
27 #include <dali/internal/event/actors/camera-actor-impl.h>
28 #include <dali/internal/event/actors/layer-impl.h>
29 #include <dali/internal/event/actors/layer-list.h>
30 #include <dali/internal/event/actors/renderable-actor-impl.h>
31 #include <dali/internal/event/common/system-overlay-impl.h>
32 #include <dali/internal/event/common/stage-impl.h>
33 #include <dali/internal/event/common/projection.h>
34 #include <dali/internal/event/images/frame-buffer-image-impl.h>
35 #include <dali/internal/event/render-tasks/render-task-impl.h>
36 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
37
38 namespace Dali
39 {
40
41 namespace Internal
42 {
43
44 namespace HitTestAlgorithm
45 {
46
47 namespace
48 {
49
50 struct HitActor
51 {
52   HitActor()
53   : actor( NULL ),
54     x( 0 ),
55     y( 0 ),
56     distance( std::numeric_limits<float>::max() ),
57     overlay( false )
58   {
59
60   }
61
62   Actor *actor;                         ///< the actor hit. (if actor hit, then initialised)
63   float x;                              ///< x position of hit (only valid if actor valid)
64   float y;                              ///< y position of hit (only valid if actor valid)
65   float distance;                       ///< distance from ray origin to hit actor
66   bool overlay;                         ///< true if the hit actor is an overlay
67
68 };
69
70 /**
71  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
72  */
73 struct HitTestFunctionWrapper : public HitTestInterface
74 {
75   /**
76    * Constructor
77    *
78    * @param[in] func HitTestFunction to call with an Actor handle.
79    */
80   HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
81   : mFunc( func )
82   {
83   }
84
85   virtual bool IsActorHittable( Actor* actor )
86   {
87     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
88   }
89
90   virtual bool DescendActorHierarchy( Actor* actor )
91   {
92     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
93   }
94
95   virtual bool DoesLayerConsumeHit( Layer* layer )
96   {
97     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
98     // where the caller may want to check for something completely different.
99     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
100     return false;
101   }
102
103   Dali::HitTestAlgorithm::HitTestFunction mFunc;
104 };
105
106 /**
107  * Used in the hit-test algorithm to check whether the actor is touchable.
108  * It is used by the touch event processor.
109  */
110 struct ActorTouchableCheck : public HitTestInterface
111 {
112   virtual bool IsActorHittable( Actor* actor )
113   {
114     return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
115            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
116   }
117
118   virtual bool DescendActorHierarchy( Actor* actor )
119   {
120     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
121            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
122   }
123
124   virtual bool DoesLayerConsumeHit( Layer* layer )
125   {
126     return layer->IsTouchConsumed();
127   }
128 };
129
130 /**
131  * Recursively hit test all the actors, without crossing into other layers.
132  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
133  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
134  * of touch vector). The closest Hit-Tested Actor is that which is returned.
135  * Exceptions to this rule are:
136  * - If the Actor is an overlay then it is considered closer than all previous
137  * overlays encountered in the hit test traversal.
138  * - When comparing against renderable parents, if Actor is the same distance
139  * or closer than it's renderable parent, then it takes priority.
140  */
141 HitActor HitTestWithinLayer( Actor& actor,
142                              const Vector4& rayOrigin,
143                              const Vector4& rayDir,
144                              bool worldOverlay,
145                              float& nearClippingPlane,
146                              float& farClippingPlane,
147                              HitTestInterface& hitCheck,
148                              bool& stencilOnLayer,
149                              bool& stencilHit,
150                              bool parentIsStencil )
151 {
152   worldOverlay |= actor.IsOverlay();
153
154   HitActor hit;
155
156   // Children should inherit the stencil draw mode
157   bool isStencil = parentIsStencil;
158
159   if ( actor.GetDrawMode() == DrawMode::STENCIL && actor.IsVisible() )
160   {
161     isStencil = true;
162     stencilOnLayer = true;
163   }
164
165   // If we are a stencil or hittable...
166   if ( isStencil || hitCheck.IsActorHittable( &actor ) )
167   {
168     Vector3 size( actor.GetCurrentSize() );
169
170     if ( size.x > 0.0f && size.y > 0.0f &&          // Ensure the actor has a valid size.
171          actor.RaySphereTest( rayOrigin, rayDir ) ) // Perform quicker ray sphere test to see if our ray is close to the actor.
172     {
173       Vector4 hitPointLocal;
174       float distance;
175
176       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
177       if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
178       {
179         if( distance >= nearClippingPlane && distance <= farClippingPlane )
180         {
181           // If the hit has happened on a stencil then register, but don't record as hit result
182           if ( isStencil )
183           {
184             stencilHit = true;
185           }
186           else
187           {
188             hit.actor = &actor;
189             hit.x = hitPointLocal.x;
190             hit.y = hitPointLocal.y;
191             hit.distance = distance;
192             hit.overlay = worldOverlay;
193           }
194         }
195       }
196     }
197   }
198
199   // If we are a stencil (or a child of a stencil) and we have already ascertained that the stencil has been hit then there is no need to hit-test the children of this stencil-actor
200   if ( isStencil && stencilHit  )
201   {
202     return hit;
203   }
204
205   // Find a child hit, until we run out of actors in the current layer.
206   HitActor childHit;
207   if( actor.GetChildCount() > 0 )
208   {
209     childHit.distance = std::numeric_limits<float>::max();
210     Dali::ActorContainer& children = actor.GetChildrenInternal();
211
212     // Hit test ALL children and calculate their distance.
213     bool parentIsRenderable = actor.IsRenderable();
214
215     for (Dali::ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
216     {
217       // Descend tree only if...
218       if ( !iter->IsLayer() &&    // Child is NOT a layer, hit testing current layer only or Child is not a layer and we've inherited the stencil draw mode
219            ( isStencil || hitCheck.DescendActorHierarchy( &GetImplementation( *iter ) ) ) ) // We are a stencil OR we can descend into child hierarchy
220       {
221         HitActor currentHit( HitTestWithinLayer( GetImplementation(*iter), rayOrigin, rayDir, worldOverlay, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, isStencil ) );
222
223         // If Current child is an overlay, then it takes priority.
224         // If it is not an overlay, and the previously hit sibling is also not an overlay, then closest takes priority.
225         // (last overlay sibling has priority as is rendered on top)
226         if ( currentHit.distance >= 0.f && (currentHit.overlay || (!childHit.overlay && currentHit.distance < childHit.distance) ) )
227         {
228           if ( !parentIsRenderable )
229           {
230             // If our parent is not renderable, then child should be hit regardless of distance.
231             childHit = currentHit;
232           }
233           else if ( currentHit.overlay || (!hit.overlay && currentHit.distance <= hit.distance) )
234           {
235             // If our parent is renderable, then child should only be hit if it is an overlay, or if it is closer than a non-overlay.
236             // (child overlay has priority as is rendered on top of it's parent)
237             childHit = currentHit;
238           }
239         }
240       }
241     }
242   }
243   return ( childHit.actor ) ? childHit : hit;
244 }
245
246 /**
247  * Return true if actor is sourceActor or a descendent of sourceActor
248  */
249 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
250 {
251   if ( &sourceActor == &actor )
252   {
253     return true;
254   }
255   else
256   {
257     Actor* parent = actor.GetParent();
258     if ( parent )
259     {
260       return IsWithinSourceActors( sourceActor, *parent );
261     }
262   }
263
264   // Not within source actors
265   return false;
266 }
267
268 /**
269  * Returns true if the layer and all of the layer's parents are visible and sensitive.
270  */
271 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
272 {
273   bool hittable( true );
274
275   if(layer.IsClipping())
276   {
277     ClippingBox box = layer.GetClippingBox();
278
279     if( screenCoordinates.x < box.x ||
280         screenCoordinates.x > box.x + box.width ||
281         screenCoordinates.y < stageSize.y - (box.y + box.height) ||
282         screenCoordinates.y > stageSize.y - box.y)
283     {
284       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
285       hittable = false;
286     }
287   }
288
289   if(hittable)
290   {
291     Actor* actor( &layer );
292
293     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
294     while ( actor && hittable )
295     {
296       if ( ! hitCheck.DescendActorHierarchy( actor ) )
297       {
298         hittable = false;
299         break;
300       }
301       actor = actor->GetParent();
302     }
303   }
304
305   return hittable;
306 }
307
308 /**
309  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
310  */
311 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
312 {
313   CameraActor* cameraActor = renderTask.GetCameraActor();
314   nearClippingPlane = cameraActor->GetNearClippingPlane();
315   farClippingPlane = cameraActor->GetFarClippingPlane();
316 }
317
318 /**
319  * Hit test a RenderTask
320  */
321 bool HitTestRenderTask( LayerList& layers,
322                         RenderTask& renderTask,
323                         Vector2 screenCoordinates,
324                         Results& results,
325                         HitTestInterface& hitCheck )
326 {
327   if ( renderTask.IsHittable( screenCoordinates ) )
328   {
329     Viewport viewport;
330     renderTask.GetViewport( viewport );
331     if( screenCoordinates.x < viewport.x ||
332         screenCoordinates.x > viewport.x + viewport.width ||
333         screenCoordinates.y < viewport.y ||
334         screenCoordinates.y > viewport.y + viewport.height )
335     {
336       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
337       return false;
338     }
339
340     float nearClippingPlane, farClippingPlane;
341     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
342
343     // Determine the layer depth of the source actor
344     Actor* sourceActor( renderTask.GetSourceActor() );
345     if ( sourceActor )
346     {
347       Dali::Layer layer( sourceActor->GetLayer() );
348       if ( layer )
349       {
350         const unsigned int sourceActorDepth( layer.GetDepth() );
351
352         CameraActor* cameraActor = renderTask.GetCameraActor();
353         bool pickingPossible = cameraActor->BuildPickingRay(
354             screenCoordinates,
355             viewport,
356             results.rayOrigin,
357             results.rayDirection );
358         if( !pickingPossible )
359         {
360           return false;
361         }
362
363         // Hit test starting with the top layer, working towards the bottom layer.
364         HitActor hit;
365         bool stencilOnLayer = false;
366         bool stencilHit = false;
367         bool layerConsumesHit = false;
368         const Vector2& stageSize = Stage::GetCurrent()->GetSize();
369
370         for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
371         {
372           Layer* layer( layers.GetLayer(i) );
373
374           HitActor previousHit = hit;
375           stencilOnLayer = false;
376           stencilHit = false;
377
378           // Ensure layer is touchable (also checks whether ancestors are also touchable)
379           if ( IsActuallyHittable ( *layer, screenCoordinates, stageSize, hitCheck ) )
380           {
381             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
382             if ( sourceActorDepth == static_cast<unsigned int>(i) )
383             {
384               // Recursively hit test the source actor & children, without crossing into other layers.
385               hit = HitTestWithinLayer( *sourceActor, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
386             }
387             else if ( IsWithinSourceActors( *sourceActor, *layer ) )
388             {
389               // Recursively hit test all the actors, without crossing into other layers.
390               hit = HitTestWithinLayer( *layer, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
391             }
392
393             // If a stencil on this layer hasn't been hit, then discard hit results for this layer if our current hit actor is renderable
394             if ( stencilOnLayer && !stencilHit &&
395                  hit.actor && hit.actor->IsRenderable() )
396             {
397               hit = previousHit;
398             }
399
400             // If this layer is set to consume the hit, then do not check any layers behind it
401             if ( hitCheck.DoesLayerConsumeHit( layer ) )
402             {
403               layerConsumesHit = true;
404               break;
405             }
406           }
407         }
408         if ( hit.actor )
409         {
410           results.renderTask = Dali::RenderTask(&renderTask);
411           results.actor = Dali::Actor(hit.actor);
412           results.actorCoordinates.x = hit.x;
413           results.actorCoordinates.y = hit.y;
414           return true; // Success
415         }
416         else if ( layerConsumesHit )
417         {
418           return true; // Also success if layer is consuming the hit
419         }
420       }
421     }
422   }
423   return false;
424 }
425
426 /**
427  * Iterate through RenderTaskList and perform hit test.
428  *
429  * @return true if we have a hit, false otherwise
430  */
431 bool HitTestForEachRenderTask( LayerList& layers,
432                                RenderTaskList& taskList,
433                                const Vector2& screenCoordinates,
434                                Results& results,
435                                HitTestInterface& hitCheck )
436 {
437   RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
438   RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
439
440   // Check onscreen tasks before offscreen ones, hit test order should be reverse of draw order (see ProcessRenderTasks() where offscreen tasks are drawn first).
441
442   // on screen
443   for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
444   {
445     RenderTask& renderTask = GetImplementation( *iter );
446     Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
447
448     // Note that if frameBufferImage is NULL we are using the default (on screen) render target
449     if(frameBufferImage)
450     {
451       ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
452
453       // on screen only
454       if(0 != id)
455       {
456         // Skip to next task
457         continue;
458       }
459     }
460
461     if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
462     {
463       // Return true when an actor is hit (or layer in our render-task consumes the hit)
464       return true; // don't bother checking off screen tasks
465     }
466   }
467
468   // off screen
469   for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
470   {
471     RenderTask& renderTask = GetImplementation( *iter );
472     Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
473
474     // Note that if frameBufferImage is NULL we are using the default (on screen) render target
475     if(frameBufferImage)
476     {
477       ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
478
479       // off screen only
480       if(0 == id)
481       {
482         // Skip to next task
483         continue;
484       }
485
486       if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
487       {
488         // Return true when an actor is hit (or a layer in our render-task consumes the hit)
489         return true;
490       }
491     }
492   }
493   return false;
494 }
495
496 } // unnamed namespace
497
498 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
499 {
500   bool wasHit( false );
501   // Hit-test the regular on-stage actors
502   RenderTaskList& taskList = stage.GetRenderTaskList();
503   LayerList& layerList = stage.GetLayerList();
504
505   Results hitTestResults;
506   HitTestFunctionWrapper hitTestFunctionWrapper( func );
507   if (  HitTestForEachRenderTask( layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
508   {
509     results.actor = hitTestResults.actor;
510     results.actorCoordinates = hitTestResults.actorCoordinates;
511     wasHit = true;
512   }
513   return wasHit;
514 }
515
516 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
517 {
518   bool wasHit( false );
519
520   // Hit-test the system-overlay actors first
521   SystemOverlay* systemOverlay = stage.GetSystemOverlayInternal();
522
523   if ( systemOverlay )
524   {
525     RenderTaskList& overlayTaskList = systemOverlay->GetOverlayRenderTasks();
526     LayerList& overlayLayerList = systemOverlay->GetLayerList();
527
528     wasHit = HitTestForEachRenderTask( overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
529   }
530
531   // Hit-test the regular on-stage actors
532   if ( !wasHit )
533   {
534     RenderTaskList& taskList = stage.GetRenderTaskList();
535     LayerList& layerList = stage.GetLayerList();
536
537     wasHit = HitTestForEachRenderTask( layerList, taskList, screenCoordinates, results, hitTestInterface );
538   }
539   return wasHit;
540 }
541
542 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
543 {
544   ActorTouchableCheck actorTouchableCheck;
545   return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
546 }
547
548 bool HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
549               Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
550 {
551   bool wasHit( false );
552   Results hitTestResults;
553
554   HitTestFunctionWrapper hitTestFunctionWrapper( func );
555   if ( HitTestRenderTask( stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
556   {
557     results.actor = hitTestResults.actor;
558     results.actorCoordinates = hitTestResults.actorCoordinates;
559     wasHit = true;
560   }
561   return wasHit;
562 }
563
564 } // namespace HitTestAlgorithm
565
566 } // namespace Internal
567
568 } // namespace Dali