[dali_2.3.27] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
1 /*
2  * Copyright (c) 2024 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/debug.h>
23 #include <dali/internal/event/actors/actor-impl.h>
24 #include <dali/internal/event/actors/camera-actor-impl.h>
25 #include <dali/internal/event/actors/layer-impl.h>
26 #include <dali/internal/event/actors/layer-list.h>
27 #include <dali/internal/event/common/projection.h>
28 #include <dali/internal/event/events/ray-test.h>
29 #include <dali/internal/event/render-tasks/render-task-impl.h>
30 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
31 #include <dali/internal/event/rendering/renderer-impl.h>
32 #include <dali/public-api/actors/layer.h>
33 #include <dali/public-api/math/vector2.h>
34 #include <dali/public-api/math/vector4.h>
35
36 namespace Dali::Internal::HitTestAlgorithm
37 {
38 namespace
39 {
40 struct HitActor
41 {
42   HitActor()
43   : actor(nullptr),
44     distance(std::numeric_limits<float>::max()),
45     depth(std::numeric_limits<int>::min())
46   {
47   }
48
49   Actor*  actor;       ///< The actor hit (if actor is hit, then this is initialised).
50   Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
51   float   distance;    ///< Distance from ray origin to hit actor.
52   int32_t depth;       ///< Depth index of this actor.
53 };
54
55 /**
56  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
57  */
58 struct HitTestFunctionWrapper : public HitTestInterface
59 {
60   /**
61    * Constructor
62    *
63    * @param[in] func HitTestFunction to call with an Actor handle.
64    */
65   HitTestFunctionWrapper(Dali::HitTestAlgorithm::HitTestFunction func)
66   : mFunc(func)
67   {
68   }
69
70   bool IsActorHittable(Actor* actor) override
71   {
72     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::CHECK_ACTOR);
73   }
74
75   bool DescendActorHierarchy(Actor* actor) override
76   {
77     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE);
78   }
79
80   bool DoesLayerConsumeHit(Layer* layer) override
81   {
82     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
83     // where the caller may want to check for something completely different.
84     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
85     return false;
86   }
87
88   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override
89   {
90     // Geometry way does not require Hittest from the client.
91     if(!isGeometry)
92     {
93       return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
94     }
95     return true;
96   }
97
98   Dali::HitTestAlgorithm::HitTestFunction mFunc;
99 };
100
101 /**
102  * Used in the hit-test algorithm to check whether the actor is touchable.
103  * It is used by the touch event processor.
104  */
105 struct ActorTouchableCheck : public HitTestInterface
106 {
107   bool IsActorHittable(Actor* actor) override
108   {
109     return (actor->GetTouchRequired() || actor->GetInterceptTouchRequired() || actor->IsTouchFocusable()) && // Does the Application or derived actor type require a touch event or a intercept touch event? or focusable by touch?
110            actor->IsHittable();                                                                              // Is actor sensitive, visible and on the scene?
111   }
112
113   bool DescendActorHierarchy(Actor* actor) override
114   {
115     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
116            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
117   }
118
119   bool DoesLayerConsumeHit(Layer* layer) override
120   {
121     return layer->IsTouchConsumed();
122   }
123
124   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override
125   {
126     // The Geometry way behaves like AllowedOnlyOwnTouch is enabled.
127     if(point.GetState() != PointState::STARTED && (isGeometry || actor->IsAllowedOnlyOwnTouch()) && ownActor != actor)
128     {
129       return false;
130     }
131     // Geometry way does not require Hittest from the client.
132     if(!isGeometry)
133     {
134       return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
135     }
136     return true;
137   }
138
139   void SetOwnActor(const Actor* actor)
140   {
141     ownActor = actor;
142   }
143   const Actor* ownActor;
144 };
145
146 /**
147  * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
148  */
149 bool IsActorExclusiveToAnotherRenderTask(const Actor&                               actor,
150                                          const RenderTask&                          renderTask,
151                                          const RenderTaskList::ExclusivesContainer& exclusives)
152
153 {
154   bool exclusiveByOtherTask = false;
155   if(exclusives.size())
156   {
157     for(const auto& exclusive : exclusives)
158     {
159       if(exclusive.actor.GetActor() == &actor)
160       {
161         if(exclusive.renderTaskPtr != &renderTask)
162         {
163           exclusiveByOtherTask = true;
164         }
165         else
166         {
167           // Fast-out if render task is itself
168           return false;
169         }
170       }
171     }
172   }
173   return exclusiveByOtherTask;
174 }
175
176 /**
177  * Checks if actor or anyone of it's parents are an overlay, until either the currentActor is reached or the root actor
178  * @param actor The child-actor and it's parents to check
179  * @param currentActor The top actor of this current branch which we should not go above
180  * @return True if the actor or a parent is an overlay, false otherwise
181  */
182 inline bool IsOnOverlay(Actor* actor, Actor* currentActor)
183 {
184   while(actor && actor != currentActor)
185   {
186     if(actor->IsOverlay())
187     {
188       return true;
189     }
190     actor = actor->GetParent();
191   }
192   return false;
193 }
194
195 /**
196  * Hit tests the given actor and updates the in/out variables appropriately
197  */
198 void HitTestActor(const RenderTask&         renderTask,
199                   const Vector4&            rayOrigin,
200                   const Vector4&            rayDir,
201                   const float&              nearClippingPlane,
202                   const float&              farClippingPlane,
203                   HitTestInterface&         hitCheck,
204                   const RayTest&            rayTest,
205                   const Integration::Point& point,
206                   const uint32_t            eventTime,
207                   bool                      clippingActor,
208                   bool                      overlayedActor,
209                   Actor&                    actor,
210                   bool&                     overlayHit,
211                   HitActor&                 hit,
212                   bool                      isGeometry)
213 {
214   if(clippingActor || hitCheck.IsActorHittable(&actor))
215   {
216     Vector3 size(actor.GetCurrentSize());
217
218     // Ensure the actor has a valid size.
219     // If so, perform a quick ray sphere test to see if our ray is close to the actor.
220     if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
221     {
222       Vector2 hitPointLocal;
223       float   distance;
224
225       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
226       if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
227       {
228         // Calculate z coordinate value in Camera Space.
229         const Matrix&  viewMatrix          = renderTask.GetCameraActor()->GetViewMatrix();
230         const Vector4& hitDir              = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
231         const float    cameraDepthDistance = (viewMatrix * hitDir).z;
232
233         // Check if cameraDepthDistance is between clipping plane
234         if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
235         {
236           if(overlayHit && !overlayedActor)
237           {
238             // If we have already hit an overlay and current actor is not an overlay ignore current actor.
239           }
240           else
241           {
242             if(overlayedActor)
243             {
244               overlayHit = true;
245             }
246
247             // If the hit actor does not want to hit, the hit-test continues.
248             if(hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime, isGeometry))
249             {
250               hit.actor       = &actor;
251               hit.hitPosition = hitPointLocal;
252               hit.distance    = distance;
253               hit.depth       = actor.GetSortingDepth();
254
255               if(actor.GetRendererCount() > 0)
256               {
257                 // Get renderer with maximum depth
258                 int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
259                 for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
260                 {
261                   int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
262                   if(depth > rendererMaxDepth)
263                   {
264                     rendererMaxDepth = depth;
265                   }
266                 }
267                 hit.depth += rendererMaxDepth;
268               }
269             }
270           }
271         }
272       }
273     }
274   }
275 }
276
277 /**
278  * When iterating through the children of an actor, this method updates the child-hit-data.
279  */
280 void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
281 {
282   bool updateChildHit = false;
283   if(currentHit.distance >= 0.0f)
284   {
285     if(layerIs3d)
286     {
287       updateChildHit = ((currentHit.depth > childHit.depth) ||
288                         ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
289     }
290     else
291     {
292       updateChildHit = currentHit.depth >= childHit.depth;
293     }
294   }
295
296   if(updateChildHit)
297   {
298     if(!parentIsRenderable || currentHit.depth > hit.depth ||
299        (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
300     {
301       childHit = currentHit;
302     }
303   }
304 }
305
306 /**
307  * Recursively hit test all the actors, without crossing into other layers.
308  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
309  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
310  * of touch vector). The closest Hit-Tested Actor is that which is returned.
311  * Exceptions to this rule are:
312  * - When comparing against renderable parents, if Actor is the same distance
313  * or closer than it's renderable parent, then it takes priority.
314  */
315 HitActor HitTestWithinLayer(Actor&                                     actor,
316                             const RenderTask&                          renderTask,
317                             const RenderTaskList::ExclusivesContainer& exclusives,
318                             const Vector4&                             rayOrigin,
319                             const Vector4&                             rayDir,
320                             const float&                               nearClippingPlane,
321                             const float&                               farClippingPlane,
322                             HitTestInterface&                          hitCheck,
323                             const bool&                                overlayed,
324                             bool&                                      overlayHit,
325                             bool                                       layerIs3d,
326                             const RayTest&                             rayTest,
327                             const Integration::Point&                  point,
328                             const uint32_t                             eventTime,
329                             std::list<Dali::Internal::Actor*>&         actorLists,
330                             bool                                       isGeometry)
331 {
332   HitActor hit;
333
334   if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
335   {
336     return hit;
337   }
338
339   // For clipping, regardless of whether we have hit this actor or not.
340   // This is used later to ensure all nested clipped children have hit
341   // all clipping actors also for them to be counted as hit.
342   const ClippingMode::Type clippingMode   = actor.GetClippingMode();
343   bool                     clippingActor  = clippingMode != ClippingMode::DISABLED;
344   bool                     overlayedActor = overlayed || actor.IsOverlay();
345
346   // If we are a clipping actor or hittable...
347   HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit, isGeometry);
348
349   // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
350   // Only do this if we're using CLIP_CHILDREN though, as children whose drawing mode is OVERLAY_2D are not clipped when CLIP_TO_BOUNDING_BOX is selected.
351   if(clippingActor && !(hit.actor) && (clippingMode == ClippingMode::CLIP_CHILDREN))
352   {
353     return hit;
354   }
355   else if(isGeometry && hit.actor)
356   {
357     // Saves the actors that can be hit as a list
358     actorLists.push_back(hit.actor);
359   }
360
361   // Find a child hit, until we run out of actors in the current layer.
362   HitActor childHit;
363   if(actor.GetChildCount() > 0)
364   {
365     // If the child touches outside the parent's size boundary, it should not be hit.
366     if(isGeometry && !actor.IsLayer())
367     {
368       Vector2 hitPointLocal;
369       float   distance;
370       if(!(rayTest.SphereTest(actor, rayOrigin, rayDir) &&
371            rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance)))
372       {
373         return hit;
374       }
375     }
376
377     childHit.distance        = std::numeric_limits<float>::max();
378     childHit.depth           = std::numeric_limits<int32_t>::min();
379     ActorContainer& children = actor.GetChildrenInternal();
380
381     // Hit test ALL children and calculate their distance.
382     bool parentIsRenderable = actor.IsRenderable();
383
384     for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
385     {
386       // Descend tree only if...
387       if(!(*iter)->IsLayer() &&                           // Child is NOT a layer, hit testing current layer only
388          (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
389       {
390         HitActor currentHit(HitTestWithinLayer((*iter->Get()),
391                                                renderTask,
392                                                exclusives,
393                                                rayOrigin,
394                                                rayDir,
395                                                nearClippingPlane,
396                                                farClippingPlane,
397                                                hitCheck,
398                                                overlayedActor,
399                                                overlayHit,
400                                                layerIs3d,
401                                                rayTest,
402                                                point,
403                                                eventTime,
404                                                actorLists,
405                                                isGeometry));
406         // Make sure the set hit actor is actually hittable. This is usually required when we have some
407         // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
408         // are hittable or not.
409         if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
410         {
411           continue;
412         }
413
414         UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
415       }
416     }
417   }
418
419   if(childHit.actor)
420   {
421     // If child has been hit & current actor is clipping to bounding box...
422     if(clippingMode == ClippingMode::CLIP_TO_BOUNDING_BOX)
423     {
424       // ...then make sure the clipping actor has actually been hit unless the child hit actor is on a child overlay.
425       if(hit.actor || IsOnOverlay(childHit.actor, &actor))
426       {
427         // Only then should we return the child hit in this scenario.
428         return childHit;
429       }
430     }
431     else
432     {
433       // no clipping concerns, return child hit.
434       return childHit;
435     }
436   }
437
438   return hit;
439 }
440
441 /**
442  * Return true if actor is sourceActor or a descendent of sourceActor
443  */
444 bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
445 {
446   if(&sourceActor == &actor)
447   {
448     return true;
449   }
450
451   Actor* parent = actor.GetParent();
452   if(parent)
453   {
454     return IsWithinSourceActors(sourceActor, *parent);
455   }
456
457   // Not within source actors
458   return false;
459 }
460
461 /**
462  * Returns true if the layer and all of the layer's parents are visible and sensitive.
463  */
464 inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
465 {
466   bool hittable(true);
467
468   if(layer.IsClipping())
469   {
470     ClippingBox box = layer.GetClippingBox();
471
472     if(screenCoordinates.x < static_cast<float>(box.x) ||
473        screenCoordinates.x > static_cast<float>(box.x + box.width) ||
474        screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
475        screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
476     {
477       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
478       hittable = false;
479     }
480   }
481
482   if(hittable)
483   {
484     Actor* actor(&layer);
485
486     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
487     while(actor && hittable)
488     {
489       if(!hitCheck.DescendActorHierarchy(actor))
490       {
491         hittable = false;
492         break;
493       }
494       actor = actor->GetParent();
495     }
496   }
497
498   return hittable;
499 }
500
501 /**
502  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
503  */
504 void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
505 {
506   CameraActor* cameraActor = renderTask.GetCameraActor();
507   nearClippingPlane        = cameraActor->GetNearClippingPlane();
508   farClippingPlane         = cameraActor->GetFarClippingPlane();
509 }
510
511 void GeoHitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
512                           const Vector2&                             sceneSize,
513                           LayerList&                                 layers,
514                           RenderTask&                                renderTask,
515                           Vector2                                    screenCoordinates,
516                           Results&                                   results,
517                           HitTestInterface&                          hitCheck,
518                           const RayTest&                             rayTest)
519 {
520   if(renderTask.IsHittable(screenCoordinates))
521   {
522     Viewport viewport;
523     renderTask.GetHittableViewport(viewport);
524
525     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
526        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
527        screenCoordinates.y < static_cast<float>(viewport.y) ||
528        screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
529     {
530       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
531       return;
532     }
533
534     float nearClippingPlane, farClippingPlane;
535     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
536
537     // Determine the layer depth of the source actor
538     Actor* sourceActor(renderTask.GetSourceActor());
539     if(sourceActor)
540     {
541       Dali::Layer sourceLayer(sourceActor->GetLayer());
542       if(sourceLayer)
543       {
544         const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
545         CameraActor*   cameraActor     = renderTask.GetCameraActor();
546         bool           pickingPossible = cameraActor->BuildPickingRay(screenCoordinates,
547                                                             viewport,
548                                                             results.rayOrigin,
549                                                             results.rayDirection);
550         if(!pickingPossible)
551         {
552           return;
553         }
554
555         // Hit test starting with the top layer, working towards the bottom layer.
556         bool overlayHit = false;
557
558         for(uint32_t i = 0; i < layers.GetLayerCount(); ++i)
559         {
560           Layer* layer(layers.GetLayer(i));
561           overlayHit = false;
562           HitActor hit;
563
564           // Ensure layer is touchable (also checks whether ancestors are also touchable)
565           if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
566           {
567             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
568             if(sourceActorDepth == i)
569             {
570               // Recursively hit test the source actor & children, without crossing into other layers.
571               hit = HitTestWithinLayer(*sourceActor,
572                                        renderTask,
573                                        exclusives,
574                                        results.rayOrigin,
575                                        results.rayDirection,
576                                        nearClippingPlane,
577                                        farClippingPlane,
578                                        hitCheck,
579                                        overlayHit,
580                                        overlayHit,
581                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
582                                        rayTest,
583                                        results.point,
584                                        results.eventTime,
585                                        results.actorLists,
586                                        true);
587             }
588             else if(IsWithinSourceActors(*sourceActor, *layer))
589             {
590               // Recursively hit test all the actors, without crossing into other layers.
591               hit = HitTestWithinLayer(*layer,
592                                        renderTask,
593                                        exclusives,
594                                        results.rayOrigin,
595                                        results.rayDirection,
596                                        nearClippingPlane,
597                                        farClippingPlane,
598                                        hitCheck,
599                                        overlayHit,
600                                        overlayHit,
601                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
602                                        rayTest,
603                                        results.point,
604                                        results.eventTime,
605                                        results.actorLists,
606                                        true);
607             }
608           }
609
610           if(hit.actor)
611           {
612             results.renderTask       = RenderTaskPtr(&renderTask);
613             results.actor            = Dali::Actor(hit.actor);
614             results.actorCoordinates = hit.hitPosition;
615           }
616         }
617       }
618     }
619   }
620   return;
621 }
622
623 /**
624  * Hit test a RenderTask
625  */
626 bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
627                        const Vector2&                             sceneSize,
628                        LayerList&                                 layers,
629                        RenderTask&                                renderTask,
630                        Vector2                                    screenCoordinates,
631                        Results&                                   results,
632                        HitTestInterface&                          hitCheck,
633                        const RayTest&                             rayTest)
634 {
635   if(renderTask.IsHittable(screenCoordinates))
636   {
637     Viewport viewport;
638     renderTask.GetHittableViewport(viewport);
639
640     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
641        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
642        screenCoordinates.y < static_cast<float>(viewport.y) ||
643        screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
644     {
645       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
646       return false;
647     }
648
649     float nearClippingPlane, farClippingPlane;
650     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
651
652     // Determine the layer depth of the source actor
653     Actor* sourceActor(renderTask.GetSourceActor());
654     if(sourceActor)
655     {
656       Dali::Layer sourceLayer(sourceActor->GetLayer());
657       if(sourceLayer)
658       {
659         const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
660
661         CameraActor* cameraActor     = renderTask.GetCameraActor();
662         bool         pickingPossible = cameraActor->BuildPickingRay(
663           screenCoordinates,
664           viewport,
665           results.rayOrigin,
666           results.rayDirection);
667         if(!pickingPossible)
668         {
669           return false;
670         }
671
672         // Hit test starting with the top layer, working towards the bottom layer.
673         HitActor hit;
674         bool     overlayHit       = false;
675         bool     layerConsumesHit = false;
676
677         // Be used when we decide to consume layer.
678         // We should not consume hit if sourceLayer is above on consumable layer. Otherwise, we should consume. So just initialize it as 0.
679         // sourceLayerIndex can be a relative value to calculate the relationship with the layer.
680         // If the layer is consumed first, sourceLayerIndex is not the actual index, but it must be guaranteed to have an index smaller than the layer.
681         // If there is a sourceLayer above the consumable layer, the sourceLayerIndex is determined and the index of the consumable layer is also determined.
682         // Then we can calculate the relationship between the two layers.
683         bool    IsHitTestWithinLayer = false;
684         int32_t sourceLayerIndex     = 0;
685         int32_t consumedLayerIndex   = -1;
686
687         for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
688         {
689           Layer* layer(layers.GetLayer(i));
690           overlayHit           = false;
691           IsHitTestWithinLayer = false;
692
693           if(sourceLayer == layer)
694           {
695             sourceLayerIndex = i;
696           }
697
698           // Ensure layer is touchable (also checks whether ancestors are also touchable)
699           if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
700           {
701             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
702             if(sourceActorDepth == static_cast<uint32_t>(i))
703             {
704               IsHitTestWithinLayer = true;
705               // Recursively hit test the source actor & children, without crossing into other layers.
706               hit = HitTestWithinLayer(*sourceActor,
707                                        renderTask,
708                                        exclusives,
709                                        results.rayOrigin,
710                                        results.rayDirection,
711                                        nearClippingPlane,
712                                        farClippingPlane,
713                                        hitCheck,
714                                        overlayHit,
715                                        overlayHit,
716                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
717                                        rayTest,
718                                        results.point,
719                                        results.eventTime,
720                                        results.actorLists,
721                                        false);
722             }
723             else if(IsWithinSourceActors(*sourceActor, *layer))
724             {
725               IsHitTestWithinLayer = true;
726               // Recursively hit test all the actors, without crossing into other layers.
727               hit = HitTestWithinLayer(*layer,
728                                        renderTask,
729                                        exclusives,
730                                        results.rayOrigin,
731                                        results.rayDirection,
732                                        nearClippingPlane,
733                                        farClippingPlane,
734                                        hitCheck,
735                                        overlayHit,
736                                        overlayHit,
737                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
738                                        rayTest,
739                                        results.point,
740                                        results.eventTime,
741                                        results.actorLists,
742                                        false);
743             }
744
745             // If this layer is set to consume the hit, then do not check any layers behind it
746             if(IsHitTestWithinLayer && hitCheck.DoesLayerConsumeHit(layer))
747             {
748               consumedLayerIndex = i;
749               layerConsumesHit   = true;
750               break;
751             }
752           }
753         }
754
755         if(hit.actor)
756         {
757           results.renderTask       = RenderTaskPtr(&renderTask);
758           results.actor            = Dali::Actor(hit.actor);
759           results.actorCoordinates = hit.hitPosition;
760
761           return true; // Success
762         }
763
764         if(layerConsumesHit)
765         {
766           // Consumes if the hitted layer is above the SourceActor's layer.
767           bool ret = sourceLayerIndex <= consumedLayerIndex;
768           if(ret)
769           {
770             DALI_LOG_RELEASE_INFO("layer is set to consume the hit\n");
771             results.renderTask = RenderTaskPtr(&renderTask);
772             results.actor      = Dali::Layer(layers.GetLayer(consumedLayerIndex));
773           }
774           return ret;
775         }
776       }
777     }
778   }
779   return false;
780 }
781
782 /**
783  * Iterate through the RenderTaskList and perform hit testing.
784  *
785  * @param[in] sceneSize The scene size the tests will be performed in
786  * @param[in] layers The list of layers to test
787  * @param[in] taskList The list of render tasks
788  * @param[out] results Ray information calculated by the camera
789  * @param[in] hitCheck The hit testing interface object to use
790  * @param[in] isGeometry Whether the scene using geometry event propagation touch and hover events.
791  * @return True if we have a hit, false otherwise
792  */
793 bool HitTestRenderTaskList(const Vector2&    sceneSize,
794                            LayerList&        layers,
795                            RenderTaskList&   taskList,
796                            const Vector2&    screenCoordinates,
797                            Results&          results,
798                            HitTestInterface& hitCheck,
799                            bool              isGeometry)
800 {
801   if(isGeometry)
802   {
803     RenderTaskList::RenderTaskContainer&          tasks      = taskList.GetTasks();
804     RenderTaskList::RenderTaskContainer::iterator endIter    = tasks.end();
805     const auto&                                   exclusives = taskList.GetExclusivesList();
806     RayTest                                       rayTest;
807
808     // Hit test order should be of draw order
809     for(RenderTaskList::RenderTaskContainer::iterator iter = tasks.begin(); endIter != iter; ++iter)
810     {
811       RenderTask& renderTask = *iter->Get();
812       GeoHitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest);
813     }
814
815     return !results.actorLists.empty();
816   }
817   else
818   {
819     RenderTaskList::RenderTaskContainer&                  tasks      = taskList.GetTasks();
820     RenderTaskList::RenderTaskContainer::reverse_iterator endIter    = tasks.rend();
821     const auto&                                           exclusives = taskList.GetExclusivesList();
822     RayTest                                               rayTest;
823
824     // Hit test order should be reverse of draw order
825     for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
826     {
827       RenderTask& renderTask = *iter->Get();
828       if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
829       {
830         // Return true when an actor is hit (or layer in our render-task consumes the hit)
831         return true;
832       }
833     }
834     return false;
835   }
836 }
837
838 /**
839  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
840  *
841  * @param[in] sceneSize The scene size the tests will be performed in
842  * @param[in] layers The list of layers to test
843  * @param[in] taskList The list of render tasks
844  * @param[out] results Ray information calculated by the camera
845  * @param[in] hitCheck The hit testing interface object to use
846  * @param[in] isGeometry Whether the scene using geometry event propagation touch and hover events.
847  * @return True if we have a hit, false otherwise
848  */
849 bool HitTestForEachRenderTask(const Vector2&    sceneSize,
850                               LayerList&        layers,
851                               RenderTaskList&   taskList,
852                               const Vector2&    screenCoordinates,
853                               Results&          results,
854                               HitTestInterface& hitCheck,
855                               bool              isGeometry)
856 {
857   bool result = false;
858
859   if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, isGeometry))
860   {
861     // Found hit.
862     result = true;
863   }
864
865   return result;
866 }
867
868 } // unnamed namespace
869
870 HitTestInterface::~HitTestInterface() = default;
871
872 bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func, bool isGeometry)
873 {
874   bool wasHit(false);
875   // Hit-test the regular on-scene actors
876   Results                hitTestResults;
877   HitTestFunctionWrapper hitTestFunctionWrapper(func);
878   if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper, isGeometry))
879   {
880     results.actor            = hitTestResults.actor;
881     results.actorCoordinates = hitTestResults.actorCoordinates;
882     wasHit                   = true;
883   }
884   return wasHit;
885 }
886
887 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface, bool isGeometry)
888 {
889   bool wasHit(false);
890
891   // Hit-test the regular on-scene actors
892   if(!wasHit)
893   {
894     wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface, isGeometry);
895   }
896   return wasHit;
897 }
898
899 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor, bool isGeometry)
900 {
901   ActorTouchableCheck actorTouchableCheck;
902   actorTouchableCheck.SetOwnActor(ownActor);
903   return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck, isGeometry);
904 }
905
906 } // namespace Dali::Internal::HitTestAlgorithm