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