Revert "[Tizen] Not execute the remove callback"
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
1 /*
2  * Copyright (c) 2023 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
37 {
38 namespace Internal
39 {
40 namespace HitTestAlgorithm
41 {
42 namespace
43 {
44 struct HitActor
45 {
46   HitActor()
47   : actor(nullptr),
48     distance(std::numeric_limits<float>::max()),
49     depth(std::numeric_limits<int>::min())
50   {
51   }
52
53   Actor*  actor;       ///< The actor hit (if actor is hit, then this is initialised).
54   Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
55   float   distance;    ///< Distance from ray origin to hit actor.
56   int32_t depth;       ///< Depth index of this actor.
57 };
58
59 /**
60  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
61  */
62 struct HitTestFunctionWrapper : public HitTestInterface
63 {
64   /**
65    * Constructor
66    *
67    * @param[in] func HitTestFunction to call with an Actor handle.
68    */
69   HitTestFunctionWrapper(Dali::HitTestAlgorithm::HitTestFunction func)
70   : mFunc(func)
71   {
72   }
73
74   bool IsActorHittable(Actor* actor) override
75   {
76     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::CHECK_ACTOR);
77   }
78
79   bool DescendActorHierarchy(Actor* actor) override
80   {
81     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE);
82   }
83
84   bool DoesLayerConsumeHit(Layer* layer) override
85   {
86     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
87     // where the caller may want to check for something completely different.
88     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
89     return false;
90   }
91
92   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
93   {
94     return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
95   }
96
97   Dali::HitTestAlgorithm::HitTestFunction mFunc;
98 };
99
100 /**
101  * Used in the hit-test algorithm to check whether the actor is touchable.
102  * It is used by the touch event processor.
103  */
104 struct ActorTouchableCheck : public HitTestInterface
105 {
106   bool IsActorHittable(Actor* actor) override
107   {
108     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?
109            actor->IsHittable();                                                                              // Is actor sensitive, visible and on the scene?
110   }
111
112   bool DescendActorHierarchy(Actor* actor) override
113   {
114     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
115            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
116   }
117
118   bool DoesLayerConsumeHit(Layer* layer) override
119   {
120     return layer->IsTouchConsumed();
121   }
122
123   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
124   {
125     if(point.GetState() != PointState::STARTED && actor->IsAllowedOnlyOwnTouch() && ownActor != actor)
126     {
127       return false;
128     }
129     return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
130   }
131
132   void SetOwnActor(const Actor* actor)
133   {
134     ownActor = actor;
135   }
136   const Actor* ownActor;
137 };
138
139 /**
140  * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
141  */
142 bool IsActorExclusiveToAnotherRenderTask(const Actor&                               actor,
143                                          const RenderTask&                          renderTask,
144                                          const RenderTaskList::ExclusivesContainer& exclusives)
145
146 {
147   if(exclusives.size())
148   {
149     for(const auto& exclusive : exclusives)
150     {
151       if((exclusive.renderTaskPtr != &renderTask) && (exclusive.actor.GetActor() == &actor))
152       {
153         return true;
154       }
155     }
156   }
157   return false;
158 }
159
160 /**
161  * Hit tests the given actor and updates the in/out variables appropriately
162  */
163 void HitTestActor(const RenderTask&         renderTask,
164                   const Vector4&            rayOrigin,
165                   const Vector4&            rayDir,
166                   const float&              nearClippingPlane,
167                   const float&              farClippingPlane,
168                   HitTestInterface&         hitCheck,
169                   const RayTest&            rayTest,
170                   const Integration::Point& point,
171                   const uint32_t            eventTime,
172                   bool                      clippingActor,
173                   bool                      overlayedActor,
174                   Actor&                    actor,
175                   bool&                     overlayHit,
176                   HitActor&                 hit)
177 {
178   if(clippingActor || hitCheck.IsActorHittable(&actor))
179   {
180     Vector3 size(actor.GetCurrentSize());
181
182     // Ensure the actor has a valid size.
183     // If so, perform a quick ray sphere test to see if our ray is close to the actor.
184     if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
185     {
186       Vector2 hitPointLocal;
187       float   distance;
188
189       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
190       if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
191       {
192         // Calculate z coordinate value in Camera Space.
193         const Matrix&  viewMatrix          = renderTask.GetCameraActor()->GetViewMatrix();
194         const Vector4& hitDir              = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
195         const float    cameraDepthDistance = (viewMatrix * hitDir).z;
196
197         // Check if cameraDepthDistance is between clipping plane
198         if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
199         {
200           if(overlayHit && !overlayedActor)
201           {
202             // If we have already hit an overlay and current actor is not an overlay ignore current actor.
203           }
204           else
205           {
206             if(overlayedActor)
207             {
208               overlayHit = true;
209             }
210
211             // If the hit actor does not want to hit, the hit-test continues.
212             if(hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime))
213             {
214               hit.actor       = &actor;
215               hit.hitPosition = hitPointLocal;
216               hit.distance    = distance;
217               hit.depth       = actor.GetSortingDepth();
218
219               if(actor.GetRendererCount() > 0)
220               {
221                 //Get renderer with maximum depth
222                 int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
223                 for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
224                 {
225                   int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
226                   if(depth > rendererMaxDepth)
227                   {
228                     rendererMaxDepth = depth;
229                   }
230                 }
231                 hit.depth += rendererMaxDepth;
232               }
233             }
234           }
235         }
236       }
237     }
238   }
239 }
240
241 /**
242  * When iterating through the children of an actor, this method updates the child-hit-data.
243  */
244 void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
245 {
246   bool updateChildHit = false;
247   if(currentHit.distance >= 0.0f)
248   {
249     if(layerIs3d)
250     {
251       updateChildHit = ((currentHit.depth > childHit.depth) ||
252                         ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
253     }
254     else
255     {
256       updateChildHit = currentHit.depth >= childHit.depth;
257     }
258   }
259
260   if(updateChildHit)
261   {
262     if(!parentIsRenderable || currentHit.depth > hit.depth ||
263        (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
264     {
265       childHit = currentHit;
266     }
267   }
268 }
269
270 /**
271  * Recursively hit test all the actors, without crossing into other layers.
272  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
273  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
274  * of touch vector). The closest Hit-Tested Actor is that which is returned.
275  * Exceptions to this rule are:
276  * - When comparing against renderable parents, if Actor is the same distance
277  * or closer than it's renderable parent, then it takes priority.
278  */
279 HitActor HitTestWithinLayer(Actor&                                     actor,
280                             const RenderTask&                          renderTask,
281                             const RenderTaskList::ExclusivesContainer& exclusives,
282                             const Vector4&                             rayOrigin,
283                             const Vector4&                             rayDir,
284                             const float&                               nearClippingPlane,
285                             const float&                               farClippingPlane,
286                             HitTestInterface&                          hitCheck,
287                             const bool&                                overlayed,
288                             bool&                                      overlayHit,
289                             bool                                       layerIs3d,
290                             const RayTest&                             rayTest,
291                             const Integration::Point&                  point,
292                             const uint32_t                             eventTime)
293 {
294   HitActor hit;
295
296   if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
297   {
298     return hit;
299   }
300
301   // For clipping, regardless of whether we have hit this actor or not.
302   // This is used later to ensure all nested clipped children have hit
303   // all clipping actors also for them to be counted as hit.
304   bool clippingActor  = actor.GetClippingMode() != ClippingMode::DISABLED;
305   bool overlayedActor = overlayed || actor.IsOverlay();
306
307   // If we are a clipping actor or hittable...
308   HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit);
309
310   // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
311   if(clippingActor && !(hit.actor))
312   {
313     return hit;
314   }
315
316   // Find a child hit, until we run out of actors in the current layer.
317   HitActor childHit;
318   if(actor.GetChildCount() > 0)
319   {
320     childHit.distance        = std::numeric_limits<float>::max();
321     childHit.depth           = std::numeric_limits<int32_t>::min();
322     ActorContainer& children = actor.GetChildrenInternal();
323
324     // Hit test ALL children and calculate their distance.
325     bool parentIsRenderable = actor.IsRenderable();
326
327     for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
328     {
329       // Descend tree only if...
330       if(!(*iter)->IsLayer() &&                           // Child is NOT a layer, hit testing current layer only
331          (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
332       {
333         HitActor currentHit(HitTestWithinLayer((*iter->Get()),
334                                                renderTask,
335                                                exclusives,
336                                                rayOrigin,
337                                                rayDir,
338                                                nearClippingPlane,
339                                                farClippingPlane,
340                                                hitCheck,
341                                                overlayedActor,
342                                                overlayHit,
343                                                layerIs3d,
344                                                rayTest,
345                                                point,
346                                                eventTime));
347
348         // Make sure the set hit actor is actually hittable. This is usually required when we have some
349         // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
350         // are hittable or not.
351         if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
352         {
353           continue;
354         }
355
356         UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
357       }
358     }
359   }
360
361   return (childHit.actor) ? childHit : hit;
362 }
363
364 /**
365  * Return true if actor is sourceActor or a descendent of sourceActor
366  */
367 bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
368 {
369   if(&sourceActor == &actor)
370   {
371     return true;
372   }
373
374   Actor* parent = actor.GetParent();
375   if(parent)
376   {
377     return IsWithinSourceActors(sourceActor, *parent);
378   }
379
380   // Not within source actors
381   return false;
382 }
383
384 /**
385  * Returns true if the layer and all of the layer's parents are visible and sensitive.
386  */
387 inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
388 {
389   bool hittable(true);
390
391   if(layer.IsClipping())
392   {
393     ClippingBox box = layer.GetClippingBox();
394
395     if(screenCoordinates.x < static_cast<float>(box.x) ||
396        screenCoordinates.x > static_cast<float>(box.x + box.width) ||
397        screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
398        screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
399     {
400       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
401       hittable = false;
402     }
403   }
404
405   if(hittable)
406   {
407     Actor* actor(&layer);
408
409     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
410     while(actor && hittable)
411     {
412       if(!hitCheck.DescendActorHierarchy(actor))
413       {
414         hittable = false;
415         break;
416       }
417       actor = actor->GetParent();
418     }
419   }
420
421   return hittable;
422 }
423
424 /**
425  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
426  */
427 void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
428 {
429   CameraActor* cameraActor = renderTask.GetCameraActor();
430   nearClippingPlane        = cameraActor->GetNearClippingPlane();
431   farClippingPlane         = cameraActor->GetFarClippingPlane();
432 }
433
434 /**
435  * Hit test a RenderTask
436  */
437 bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
438                        const Vector2&                             sceneSize,
439                        LayerList&                                 layers,
440                        RenderTask&                                renderTask,
441                        Vector2                                    screenCoordinates,
442                        Results&                                   results,
443                        HitTestInterface&                          hitCheck,
444                        const RayTest&                             rayTest)
445 {
446   if(renderTask.IsHittable(screenCoordinates))
447   {
448     Viewport viewport;
449     renderTask.GetViewport(viewport);
450     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
451        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
452        screenCoordinates.y < static_cast<float>(viewport.y) ||
453        screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
454     {
455       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
456       return false;
457     }
458
459     float nearClippingPlane, farClippingPlane;
460     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
461
462     // Determine the layer depth of the source actor
463     Actor* sourceActor(renderTask.GetSourceActor());
464     if(sourceActor)
465     {
466       Dali::Layer sourceLayer(sourceActor->GetLayer());
467       if(sourceLayer)
468       {
469         const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
470
471         CameraActor* cameraActor     = renderTask.GetCameraActor();
472         bool         pickingPossible = cameraActor->BuildPickingRay(
473           screenCoordinates,
474           viewport,
475           results.rayOrigin,
476           results.rayDirection);
477         if(!pickingPossible)
478         {
479           return false;
480         }
481
482         // Hit test starting with the top layer, working towards the bottom layer.
483         HitActor hit;
484         bool     overlayHit       = false;
485         bool     layerConsumesHit = false;
486
487         for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
488         {
489           Layer* layer(layers.GetLayer(i));
490           overlayHit = false;
491
492           // Ensure layer is touchable (also checks whether ancestors are also touchable)
493           if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
494           {
495             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
496             if(sourceActorDepth == static_cast<uint32_t>(i))
497             {
498               // Recursively hit test the source actor & children, without crossing into other layers.
499               hit = HitTestWithinLayer(*sourceActor,
500                                        renderTask,
501                                        exclusives,
502                                        results.rayOrigin,
503                                        results.rayDirection,
504                                        nearClippingPlane,
505                                        farClippingPlane,
506                                        hitCheck,
507                                        overlayHit,
508                                        overlayHit,
509                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
510                                        rayTest,
511                                        results.point,
512                                        results.eventTime);
513             }
514             else if(IsWithinSourceActors(*sourceActor, *layer))
515             {
516               // Recursively hit test all the actors, without crossing into other layers.
517               hit = HitTestWithinLayer(*layer,
518                                        renderTask,
519                                        exclusives,
520                                        results.rayOrigin,
521                                        results.rayDirection,
522                                        nearClippingPlane,
523                                        farClippingPlane,
524                                        hitCheck,
525                                        overlayHit,
526                                        overlayHit,
527                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
528                                        rayTest,
529                                        results.point,
530                                        results.eventTime);
531             }
532
533             // If this layer is set to consume the hit, then do not check any layers behind it
534             if(hitCheck.DoesLayerConsumeHit(layer))
535             {
536               // Consume the hit if this layer is same as SourceActor's layer
537               layerConsumesHit = (sourceLayer == Dali::Layer(layer));
538               break;
539             }
540           }
541         }
542
543         if(hit.actor)
544         {
545           results.renderTask       = RenderTaskPtr(&renderTask);
546           results.actor            = Dali::Actor(hit.actor);
547           results.actorCoordinates = hit.hitPosition;
548
549           return true; // Success
550         }
551
552         if(layerConsumesHit)
553         {
554           return true; // Also success if layer is consuming the hit
555         }
556       }
557     }
558   }
559   return false;
560 }
561
562 /**
563  * Iterate through the RenderTaskList and perform hit testing.
564  *
565  * @param[in] sceneSize The scene size the tests will be performed in
566  * @param[in] layers The list of layers to test
567  * @param[in] taskList The list of render tasks
568  * @param[out] results Ray information calculated by the camera
569  * @param[in] hitCheck The hit testing interface object to use
570  * @return True if we have a hit, false otherwise
571  */
572 bool HitTestRenderTaskList(const Vector2&    sceneSize,
573                            LayerList&        layers,
574                            RenderTaskList&   taskList,
575                            const Vector2&    screenCoordinates,
576                            Results&          results,
577                            HitTestInterface& hitCheck)
578 {
579   RenderTaskList::RenderTaskContainer&                  tasks      = taskList.GetTasks();
580   RenderTaskList::RenderTaskContainer::reverse_iterator endIter    = tasks.rend();
581   const auto&                                           exclusives = taskList.GetExclusivesList();
582   RayTest                                               rayTest;
583
584   // Hit test order should be reverse of draw order
585   for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
586   {
587     RenderTask& renderTask = *iter->Get();
588     if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
589     {
590       // Return true when an actor is hit (or layer in our render-task consumes the hit)
591       return true;
592     }
593   }
594
595   return false;
596 }
597
598 /**
599  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
600  *
601  * @param[in] sceneSize The scene size the tests will be performed in
602  * @param[in] layers The list of layers to test
603  * @param[in] taskList The list of render tasks
604  * @param[out] results Ray information calculated by the camera
605  * @param[in] hitCheck The hit testing interface object to use
606  * @return True if we have a hit, false otherwise
607  */
608 bool HitTestForEachRenderTask(const Vector2&    sceneSize,
609                               LayerList&        layers,
610                               RenderTaskList&   taskList,
611                               const Vector2&    screenCoordinates,
612                               Results&          results,
613                               HitTestInterface& hitCheck)
614 {
615   bool result = false;
616
617   if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck))
618   {
619     // Found hit.
620     result = true;
621   }
622
623   return result;
624 }
625
626 } // unnamed namespace
627
628 HitTestInterface::~HitTestInterface() = default;
629
630 bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func)
631 {
632   bool wasHit(false);
633   // Hit-test the regular on-scene actors
634   Results                hitTestResults;
635   HitTestFunctionWrapper hitTestFunctionWrapper(func);
636   if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper))
637   {
638     results.actor            = hitTestResults.actor;
639     results.actorCoordinates = hitTestResults.actorCoordinates;
640     wasHit                   = true;
641   }
642   return wasHit;
643 }
644
645 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface)
646 {
647   bool wasHit(false);
648
649   // Hit-test the regular on-scene actors
650   if(!wasHit)
651   {
652     wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface);
653   }
654   return wasHit;
655 }
656
657 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor)
658 {
659   ActorTouchableCheck actorTouchableCheck;
660   actorTouchableCheck.SetOwnActor(ownActor);
661   return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck);
662 }
663
664 } // namespace HitTestAlgorithm
665
666 } // namespace Internal
667
668 } // namespace Dali