Conversion to Apache 2.0 license
[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   Dali::HitTestAlgorithm::HitTestFunction mFunc;
96 };
97
98 /**
99  * Used in the hit-test algorithm to check whether the actor is touchable.
100  * It is used by the touch event processor.
101  */
102 struct ActorTouchableCheck : public HitTestInterface
103 {
104   virtual bool IsActorHittable( Actor* actor )
105   {
106     return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
107            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
108   }
109
110   virtual bool DescendActorHierarchy( Actor* actor )
111   {
112     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
113            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
114   }
115 };
116
117 /**
118  * Recursively hit test all the actors, without crossing into other layers.
119  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
120  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
121  * of touch vector). The closest Hit-Tested Actor is that which is returned.
122  * Exceptions to this rule are:
123  * - If the Actor is an overlay then it is considered closer than all previous
124  * overlays encountered in the hit test traversal.
125  * - When comparing against renderable parents, if Actor is the same distance
126  * or closer than it's renderable parent, then it takes priority.
127  */
128 HitActor HitTestWithinLayer( Actor& actor,
129                              const Vector4& rayOrigin,
130                              const Vector4& rayDir,
131                              bool worldOverlay,
132                              float& nearClippingPlane,
133                              float& farClippingPlane,
134                              HitTestInterface& hitCheck,
135                              bool& stencilOnLayer,
136                              bool& stencilHit,
137                              bool parentIsStencil )
138 {
139   worldOverlay |= actor.IsOverlay();
140
141   HitActor hit;
142
143   // Children should inherit the stencil draw mode
144   bool isStencil = parentIsStencil;
145
146   if ( actor.GetDrawMode() == DrawMode::STENCIL && actor.IsVisible() )
147   {
148     isStencil = true;
149     stencilOnLayer = true;
150   }
151
152   // If we are a stencil or hittable...
153   if ( isStencil || hitCheck.IsActorHittable( &actor ) )
154   {
155     Vector3 size( actor.GetCurrentSize() );
156
157     if ( size.x > 0.0f && size.y > 0.0f &&          // Ensure the actor has a valid size.
158          actor.RaySphereTest( rayOrigin, rayDir ) ) // Perform quicker ray sphere test to see if our ray is close to the actor.
159     {
160       Vector4 hitPointLocal;
161       float distance;
162
163       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
164       if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
165       {
166         if( distance >= nearClippingPlane && distance <= farClippingPlane )
167         {
168           // If the hit has happened on a stencil then register, but don't record as hit result
169           if ( isStencil )
170           {
171             stencilHit = true;
172           }
173           else
174           {
175             hit.actor = &actor;
176             hit.x = hitPointLocal.x;
177             hit.y = hitPointLocal.y;
178             hit.distance = distance;
179             hit.overlay = worldOverlay;
180           }
181         }
182       }
183     }
184   }
185
186   // If there is a stencil on this layer and we've also registered a hit, then don't both searching any children
187   if ( stencilHit && hit.actor )
188   {
189     return hit;
190   }
191
192   // Find a child hit, until we run out of actors in the current layer.
193   HitActor childHit;
194   if( actor.GetChildCount() > 0 )
195   {
196     childHit.distance = std::numeric_limits<float>::max();
197     Dali::ActorContainer& children = actor.GetChildrenInternal();
198
199     // Hit test ALL children and calculate their distance.
200     bool parentIsRenderable = actor.IsRenderable();
201
202     for (Dali::ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
203     {
204       // Descend tree only if...
205       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
206            ( isStencil || hitCheck.DescendActorHierarchy( &GetImplementation( *iter ) ) ) ) // We are a stencil OR we can descend into child hierarchy
207       {
208         HitActor currentHit( HitTestWithinLayer( GetImplementation(*iter), rayOrigin, rayDir, worldOverlay, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, isStencil ) );
209
210         // If Current child is an overlay, then it takes priority.
211         // If it is not an overlay, and the previously hit sibling is also not an overlay, then closest takes priority.
212         // (last overlay sibling has priority as is rendered on top)
213         if ( currentHit.distance >= 0.f && (currentHit.overlay || (!childHit.overlay && currentHit.distance < childHit.distance) ) )
214         {
215           if ( !parentIsRenderable )
216           {
217             // If our parent is not renderable, then child should be hit regardless of distance.
218             childHit = currentHit;
219           }
220           else if ( currentHit.overlay || (!hit.overlay && currentHit.distance <= hit.distance) )
221           {
222             // 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.
223             // (child overlay has priority as is rendered on top of it's parent)
224             childHit = currentHit;
225           }
226         }
227       }
228     }
229   }
230   return ( childHit.actor ) ? childHit : hit;
231 }
232
233 /**
234  * Return true if actor is sourceActor or a descendent of sourceActor
235  */
236 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
237 {
238   if ( &sourceActor == &actor )
239   {
240     return true;
241   }
242   else
243   {
244     Actor* parent = actor.GetParent();
245     if ( parent )
246     {
247       return IsWithinSourceActors( sourceActor, *parent );
248     }
249   }
250
251   // Not within source actors
252   return false;
253 }
254
255 /**
256  * Returns true if the layer and all of the layer's parents are visible and sensitive.
257  */
258 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
259 {
260   bool hittable( true );
261
262   if(layer.IsClipping())
263   {
264     ClippingBox box = layer.GetClippingBox();
265
266     if( screenCoordinates.x < box.x ||
267         screenCoordinates.x > box.x + box.width ||
268         screenCoordinates.y < stageSize.y - (box.y + box.height) ||
269         screenCoordinates.y > stageSize.y - box.y)
270     {
271       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
272       hittable = false;
273     }
274   }
275
276   if(hittable)
277   {
278     Actor* actor( &layer );
279
280     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
281     while ( actor && hittable )
282     {
283       if ( ! hitCheck.DescendActorHierarchy( actor ) )
284       {
285         hittable = false;
286         break;
287       }
288       actor = actor->GetParent();
289     }
290   }
291
292   return hittable;
293 }
294
295 /**
296  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
297  */
298 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
299 {
300   CameraActor* cameraActor = renderTask.GetCameraActor();
301   nearClippingPlane = cameraActor->GetNearClippingPlane();
302   farClippingPlane = cameraActor->GetFarClippingPlane();
303 }
304
305 /**
306  * Hit test a RenderTask
307  */
308 bool HitTestRenderTask( LayerList& layers,
309                         RenderTask& renderTask,
310                         Vector2 screenCoordinates,
311                         Results& results,
312                         HitTestInterface& hitCheck )
313 {
314   if ( renderTask.IsHittable( screenCoordinates ) )
315   {
316     Viewport viewport;
317     renderTask.GetViewport( viewport );
318     if( screenCoordinates.x < viewport.x ||
319         screenCoordinates.x > viewport.x + viewport.width ||
320         screenCoordinates.y < viewport.y ||
321         screenCoordinates.y > viewport.y + viewport.height )
322     {
323       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
324       return false;
325     }
326
327     float nearClippingPlane, farClippingPlane;
328     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
329
330     // Determine the layer depth of the source actor
331     Actor* sourceActor( renderTask.GetSourceActor() );
332     if ( sourceActor )
333     {
334       Dali::Layer layer( sourceActor->GetLayer() );
335       if ( layer )
336       {
337         const unsigned int sourceActorDepth( layer.GetDepth() );
338
339         CameraActor* cameraActor = renderTask.GetCameraActor();
340         bool pickingPossible = cameraActor->BuildPickingRay(
341             screenCoordinates,
342             viewport,
343             results.rayOrigin,
344             results.rayDirection );
345         if( !pickingPossible )
346         {
347           return false;
348         }
349
350         // Hit test starting with the top layer, working towards the bottom layer.
351         HitActor hit;
352         bool stencilOnLayer = false;
353         bool stencilHit = false;
354         const Vector2& stageSize = Stage::GetCurrent()->GetSize();
355
356         for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
357         {
358           Layer* layer( layers.GetLayer(i) );
359
360           HitActor previousHit = hit;
361           stencilOnLayer = false;
362           stencilHit = false;
363
364           // Ensure layer is touchable (also checks whether ancestors are also touchable)
365           if ( IsActuallyHittable ( *layer, screenCoordinates, stageSize, hitCheck ) )
366           {
367             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
368             if ( sourceActorDepth == static_cast<unsigned int>(i) )
369             {
370               // Recursively hit test the source actor & children, without crossing into other layers.
371               hit = HitTestWithinLayer( *sourceActor, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
372             }
373             else if ( IsWithinSourceActors( *sourceActor, *layer ) )
374             {
375               // Recursively hit test all the actors, without crossing into other layers.
376               hit = HitTestWithinLayer( *layer, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
377             }
378             // If a stencil on this layer hasn't been hit, then discard hit results for this layer
379             if ( stencilOnLayer && !stencilHit )
380             {
381              hit = previousHit;
382             }
383           }
384         }
385         if ( hit.actor )
386         {
387           results.renderTask = Dali::RenderTask(&renderTask);
388           results.actor = Dali::Actor(hit.actor);
389           results.actorCoordinates.x = hit.x;
390           results.actorCoordinates.y = hit.y;
391           return true; // Success
392         }
393       }
394     }
395   }
396   return false;
397 }
398
399 /**
400  * Iterate through RenderTaskList and perform hit test.
401  */
402
403 void HitTestForEachRenderTask( LayerList& layers,
404                                RenderTaskList& taskList,
405                                const Vector2& screenCoordinates,
406                                Results& results,
407                                HitTestInterface& hitCheck )
408 {
409   RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
410   RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
411
412   // Check onscreen tasks before offscreen ones, hit test order should be reverse of draw order (see ProcessRenderTasks() where offscreen tasks are drawn first).
413
414   // on screen
415   for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
416   {
417     RenderTask& renderTask = GetImplementation( *iter );
418     Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
419
420     // Note that if frameBufferImage is NULL we are using the default (on screen) render target
421     if(frameBufferImage)
422     {
423       ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
424
425       // on screen only
426       if(0 != id)
427       {
428         // Skip to next task
429         continue;
430       }
431     }
432
433     if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
434     {
435       // Exit when an actor is hit
436       return; // don't bother checking off screen tasks
437     }
438   }
439
440   // off screen
441   for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
442   {
443     RenderTask& renderTask = GetImplementation( *iter );
444     Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
445
446     // Note that if frameBufferImage is NULL we are using the default (on screen) render target
447     if(frameBufferImage)
448     {
449       ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
450
451       // off screen only
452       if(0 == id)
453       {
454         // Skip to next task
455         continue;
456       }
457
458       if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
459       {
460         // Exit when an actor is hit
461         break;
462       }
463     }
464   }
465 }
466
467 } // unnamed namespace
468
469 void HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
470 {
471   // Hit-test the regular on-stage actors
472   RenderTaskList& taskList = stage.GetRenderTaskList();
473   LayerList& layerList = stage.GetLayerList();
474
475   Results hitTestResults;
476   HitTestFunctionWrapper hitTestFunctionWrapper( func );
477   HitTestForEachRenderTask( layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper );
478
479   results.actor = hitTestResults.actor;
480   results.actorCoordinates = hitTestResults.actorCoordinates;
481 }
482
483 void HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
484 {
485   // Hit-test the system-overlay actors first
486   SystemOverlay* systemOverlay = stage.GetSystemOverlayInternal();
487
488   if ( systemOverlay )
489   {
490     RenderTaskList& overlayTaskList = systemOverlay->GetOverlayRenderTasks();
491     LayerList& overlayLayerList = systemOverlay->GetLayerList();
492
493     HitTestForEachRenderTask( overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
494   }
495
496   // Hit-test the regular on-stage actors
497   if ( !results.actor )
498   {
499     RenderTaskList& taskList = stage.GetRenderTaskList();
500     LayerList& layerList = stage.GetLayerList();
501
502     HitTestForEachRenderTask( layerList, taskList, screenCoordinates, results, hitTestInterface );
503   }
504 }
505
506 void HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
507 {
508   ActorTouchableCheck actorTouchableCheck;
509   HitTest( stage, screenCoordinates, results, actorTouchableCheck );
510 }
511
512 void HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
513               Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
514 {
515   Results hitTestResults;
516   HitTestFunctionWrapper hitTestFunctionWrapper( func );
517   HitTestRenderTask( stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper );
518   results.actor = hitTestResults.actor;
519   results.actorCoordinates = hitTestResults.actorCoordinates;
520 }
521
522 } // namespace HitTestAlgorithm
523
524 } // namespace Internal
525
526 } // namespace Dali