2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
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>
36 namespace Dali::Internal::HitTestAlgorithm
44 distance(std::numeric_limits<float>::max()),
45 depth(std::numeric_limits<int>::min())
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.
56 * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
58 struct HitTestFunctionWrapper : public HitTestInterface
63 * @param[in] func HitTestFunction to call with an Actor handle.
65 HitTestFunctionWrapper(Dali::HitTestAlgorithm::HitTestFunction func)
70 bool IsActorHittable(Actor* actor) override
72 return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::CHECK_ACTOR);
75 bool DescendActorHierarchy(Actor* actor) override
77 return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE);
80 bool DoesLayerConsumeHit(Layer* layer) override
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
88 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
90 // Geometry way does not require Hittest from the client.
91 if(propagationType == Integration::Scene::TouchPropagationType::PARENT)
93 return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
98 Dali::HitTestAlgorithm::HitTestFunction mFunc;
102 * Used in the hit-test algorithm to check whether the actor is touchable.
103 * It is used by the touch event processor.
105 struct ActorTouchableCheck : public HitTestInterface
107 bool IsActorHittable(Actor* actor) override
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?
113 bool DescendActorHierarchy(Actor* actor) override
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.
119 bool DoesLayerConsumeHit(Layer* layer) override
121 return layer->IsTouchConsumed();
124 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
126 // The Geometry way behaves like AllowedOnlyOwnTouch is enabled.
127 if(point.GetState() != PointState::STARTED && (propagationType == Integration::Scene::TouchPropagationType::GEOMETRY || actor->IsAllowedOnlyOwnTouch()) && ownActor != actor)
131 // Geometry way does not require Hittest from the client.
132 if(propagationType == Integration::Scene::TouchPropagationType::PARENT)
134 return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
139 void SetOwnActor(const Actor* actor)
143 const Actor* ownActor;
147 * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
149 bool IsActorExclusiveToAnotherRenderTask(const Actor& actor,
150 const RenderTask& renderTask,
151 const RenderTaskList::ExclusivesContainer& exclusives)
154 bool exclusiveByOtherTask = false;
155 if(exclusives.size())
157 for(const auto& exclusive : exclusives)
159 if(exclusive.actor.GetActor() == &actor)
161 if(exclusive.renderTaskPtr != &renderTask)
163 exclusiveByOtherTask = true;
167 // Fast-out if render task is itself
173 return exclusiveByOtherTask;
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
182 inline bool IsOnOverlay(Actor* actor, Actor* currentActor)
184 while(actor && actor != currentActor)
186 if(actor->IsOverlay())
190 actor = actor->GetParent();
196 * Hit tests the given actor and updates the in/out variables appropriately
198 bool 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,
212 const Integration::Scene::TouchPropagationType propagationType)
214 bool isClippingOrHittable = clippingActor || hitCheck.IsActorHittable(&actor);
215 bool isGeometry = propagationType == Integration::Scene::TouchPropagationType::GEOMETRY;
216 if(isClippingOrHittable || isGeometry)
218 Vector3 size(actor.GetCurrentSize());
220 // Ensure the actor has a valid size.
221 // If so, perform a quick ray sphere test to see if our ray is close to the actor.
222 if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
224 Vector2 hitPointLocal;
227 // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
228 if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
230 // Calculate z coordinate value in Camera Space.
231 const Matrix& viewMatrix = renderTask.GetCameraActor()->GetViewMatrix();
232 const Vector4& hitDir = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
233 const float cameraDepthDistance = (viewMatrix * hitDir).z;
235 // Check if cameraDepthDistance is between clipping plane
236 if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
238 if(isGeometry && actor.GetParent())
240 // If the child touches outside the parent's size boundary, it should not be hit.
241 if(!overlayedActor && !clippingActor && !actor.GetParent()->IsLayer())
243 Vector2 hitPointLocal;
245 if(!(rayTest.SphereTest(*actor.GetParent(), rayOrigin, rayDir) &&
246 rayTest.ActorTest(*actor.GetParent(), rayOrigin, rayDir, hitPointLocal, distance)))
253 if(overlayHit && !overlayedActor)
255 // If we have already hit an overlay and current actor is not an overlay ignore current actor.
264 // If the hit actor does not want to hit, the hit-test continues.
265 if(isClippingOrHittable && hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime, propagationType))
268 hit.hitPosition = hitPointLocal;
269 hit.distance = distance;
270 hit.depth = actor.GetSortingDepth();
272 if(actor.GetRendererCount() > 0)
274 // Get renderer with maximum depth
275 int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
276 for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
278 int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
279 if(depth > rendererMaxDepth)
281 rendererMaxDepth = depth;
284 hit.depth += rendererMaxDepth;
296 * When iterating through the children of an actor, this method updates the child-hit-data.
298 void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
300 bool updateChildHit = false;
301 if(currentHit.distance >= 0.0f)
305 updateChildHit = ((currentHit.depth > childHit.depth) ||
306 ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
310 updateChildHit = currentHit.depth >= childHit.depth;
316 if(!parentIsRenderable || currentHit.depth > hit.depth ||
317 (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
319 childHit = currentHit;
325 * Recursively hit test all the actors, without crossing into other layers.
326 * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
327 * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
328 * of touch vector). The closest Hit-Tested Actor is that which is returned.
329 * Exceptions to this rule are:
330 * - When comparing against renderable parents, if Actor is the same distance
331 * or closer than it's renderable parent, then it takes priority.
333 HitActor HitTestWithinLayer(Actor& actor,
334 const RenderTask& renderTask,
335 const RenderTaskList::ExclusivesContainer& exclusives,
336 const Vector4& rayOrigin,
337 const Vector4& rayDir,
338 const float& nearClippingPlane,
339 const float& farClippingPlane,
340 HitTestInterface& hitCheck,
341 const bool& overlayed,
344 const RayTest& rayTest,
345 const Integration::Point& point,
346 const uint32_t eventTime,
347 std::list<Dali::Internal::Actor*>& actorLists,
348 const Integration::Scene::TouchPropagationType propagationType)
352 if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
357 // For clipping, regardless of whether we have hit this actor or not.
358 // This is used later to ensure all nested clipped children have hit
359 // all clipping actors also for them to be counted as hit.
360 const ClippingMode::Type clippingMode = actor.GetClippingMode();
361 bool clippingActor = clippingMode != ClippingMode::DISABLED;
362 bool overlayedActor = overlayed || actor.IsOverlay();
364 // If we are a clipping actor or hittable...
365 if(!HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit, propagationType))
370 // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
371 // 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.
372 if(clippingActor && !(hit.actor) && (clippingMode == ClippingMode::CLIP_CHILDREN))
376 else if(propagationType == Integration::Scene::TouchPropagationType::GEOMETRY && hit.actor)
378 // Saves the actors that can be hit as a list
379 actorLists.push_back(hit.actor);
382 // Find a child hit, until we run out of actors in the current layer.
384 if(actor.GetChildCount() > 0)
386 childHit.distance = std::numeric_limits<float>::max();
387 childHit.depth = std::numeric_limits<int32_t>::min();
388 ActorContainer& children = actor.GetChildrenInternal();
390 // Hit test ALL children and calculate their distance.
391 bool parentIsRenderable = actor.IsRenderable();
393 for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
395 // Descend tree only if...
396 if(!(*iter)->IsLayer() && // Child is NOT a layer, hit testing current layer only
397 (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
399 HitActor currentHit(HitTestWithinLayer((*iter->Get()),
415 // Make sure the set hit actor is actually hittable. This is usually required when we have some
416 // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
417 // are hittable or not.
418 if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
423 UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
430 // If child has been hit & current actor is clipping to bounding box...
431 if(clippingMode == ClippingMode::CLIP_TO_BOUNDING_BOX)
433 // ...then make sure the clipping actor has actually been hit unless the child hit actor is on a child overlay.
434 if(hit.actor || IsOnOverlay(childHit.actor, &actor))
436 // Only then should we return the child hit in this scenario.
442 // no clipping concerns, return child hit.
451 * Return true if actor is sourceActor or a descendent of sourceActor
453 bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
455 if(&sourceActor == &actor)
460 Actor* parent = actor.GetParent();
463 return IsWithinSourceActors(sourceActor, *parent);
466 // Not within source actors
471 * Returns true if the actor and all of the actor's parents are hittable.
473 bool IsActorActuallyHittable(Actor* actor, HitTestInterface& hitCheck)
475 Actor* currentActor = actor;
476 // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
479 if(!hitCheck.DescendActorHierarchy(currentActor))
483 currentActor = currentActor->GetParent();
490 * Returns true if the layer and all of the layer's parents are hittable.
492 inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
496 if(layer.IsClipping())
498 ClippingBox box = layer.GetClippingBox();
500 if(screenCoordinates.x < static_cast<float>(box.x) ||
501 screenCoordinates.x > static_cast<float>(box.x + box.width) ||
502 screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
503 screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
505 // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
512 Actor* actor(&layer);
513 hittable = IsActorActuallyHittable(actor, hitCheck);
520 * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
522 void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
524 CameraActor* cameraActor = renderTask.GetCameraActor();
525 nearClippingPlane = cameraActor->GetNearClippingPlane();
526 farClippingPlane = cameraActor->GetFarClippingPlane();
529 void GeoHitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
530 const Vector2& sceneSize,
532 RenderTask& renderTask,
533 Vector2 screenCoordinates,
535 HitTestInterface& hitCheck,
536 const RayTest& rayTest)
538 if(renderTask.IsHittable(screenCoordinates))
541 renderTask.GetHittableViewport(viewport);
543 if(screenCoordinates.x < static_cast<float>(viewport.x) ||
544 screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
545 screenCoordinates.y < static_cast<float>(viewport.y) ||
546 screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
548 // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
552 float nearClippingPlane, farClippingPlane;
553 GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
555 // Determine the layer depth of the source actor
556 Actor* sourceActor(renderTask.GetSourceActor());
559 Dali::Layer sourceLayer(sourceActor->GetLayer());
562 const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
563 CameraActor* cameraActor = renderTask.GetCameraActor();
564 bool pickingPossible = cameraActor->BuildPickingRay(screenCoordinates,
567 results.rayDirection);
573 // Hit test starting with the top layer, working towards the bottom layer.
574 bool overlayHit = false;
576 for(uint32_t i = 0; i < layers.GetLayerCount(); ++i)
578 Layer* layer(layers.GetLayer(i));
582 // Ensure layer is touchable (also checks whether ancestors are also touchable)
583 if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
585 // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
586 if(sourceActorDepth == i)
588 // Recursively hit test the source actor & children, without crossing into other layers.
589 hit = HitTestWithinLayer(*sourceActor,
593 results.rayDirection,
599 layer->GetBehavior() == Dali::Layer::LAYER_3D,
604 Integration::Scene::TouchPropagationType::GEOMETRY);
606 else if(IsWithinSourceActors(*sourceActor, *layer))
608 // Recursively hit test all the actors, without crossing into other layers.
609 hit = HitTestWithinLayer(*layer,
613 results.rayDirection,
619 layer->GetBehavior() == Dali::Layer::LAYER_3D,
624 Integration::Scene::TouchPropagationType::GEOMETRY);
630 results.renderTask = RenderTaskPtr(&renderTask);
631 results.actor = Dali::Actor(hit.actor);
632 results.actorCoordinates = hit.hitPosition;
642 * Hit test a RenderTask
644 bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
645 const Vector2& sceneSize,
647 RenderTask& renderTask,
648 Vector2 screenCoordinates,
650 HitTestInterface& hitCheck,
651 const RayTest& rayTest)
653 if(renderTask.IsHittable(screenCoordinates))
656 renderTask.GetHittableViewport(viewport);
658 if(screenCoordinates.x < static_cast<float>(viewport.x) ||
659 screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
660 screenCoordinates.y < static_cast<float>(viewport.y) ||
661 screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
663 // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
667 float nearClippingPlane, farClippingPlane;
668 GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
670 // Determine the layer depth of the source actor
671 Actor* sourceActor(renderTask.GetSourceActor());
673 // Check the source actor is actually hittable or not.
674 if(sourceActor && IsActorActuallyHittable(sourceActor, hitCheck))
676 Dali::Layer sourceLayer(sourceActor->GetLayer());
679 const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
681 CameraActor* cameraActor = renderTask.GetCameraActor();
682 bool pickingPossible = cameraActor->BuildPickingRay(
686 results.rayDirection);
692 // Hit test starting with the top layer, working towards the bottom layer.
694 bool overlayHit = false;
695 bool layerConsumesHit = false;
697 // Be used when we decide to consume layer.
698 // We should not consume hit if sourceLayer is above on consumable layer. Otherwise, we should consume. So just initialize it as 0.
699 // sourceLayerIndex can be a relative value to calculate the relationship with the layer.
700 // 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.
701 // If there is a sourceLayer above the consumable layer, the sourceLayerIndex is determined and the index of the consumable layer is also determined.
702 // Then we can calculate the relationship between the two layers.
703 bool IsHitTestWithinLayer = false;
704 int32_t sourceLayerIndex = 0;
705 int32_t consumedLayerIndex = -1;
707 for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
709 Layer* layer(layers.GetLayer(i));
711 IsHitTestWithinLayer = false;
713 if(sourceLayer == layer)
715 sourceLayerIndex = i;
718 // Ensure layer is touchable (also checks whether ancestors are also touchable)
719 if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
721 // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
722 if(sourceActorDepth == static_cast<uint32_t>(i))
724 IsHitTestWithinLayer = true;
725 // Recursively hit test the source actor & children, without crossing into other layers.
726 hit = HitTestWithinLayer(*sourceActor,
730 results.rayDirection,
736 layer->GetBehavior() == Dali::Layer::LAYER_3D,
741 Integration::Scene::TouchPropagationType::PARENT);
743 else if(IsWithinSourceActors(*sourceActor, *layer))
745 IsHitTestWithinLayer = true;
746 // Recursively hit test all the actors, without crossing into other layers.
747 hit = HitTestWithinLayer(*layer,
751 results.rayDirection,
757 layer->GetBehavior() == Dali::Layer::LAYER_3D,
762 Integration::Scene::TouchPropagationType::PARENT);
765 // If this layer is set to consume the hit, then do not check any layers behind it
766 if(IsHitTestWithinLayer && hitCheck.DoesLayerConsumeHit(layer))
768 consumedLayerIndex = i;
769 layerConsumesHit = true;
777 results.renderTask = RenderTaskPtr(&renderTask);
778 results.actor = Dali::Actor(hit.actor);
779 results.actorCoordinates = hit.hitPosition;
781 return true; // Success
786 // Consumes if the hitted layer is above the SourceActor's layer.
787 bool ret = sourceLayerIndex <= consumedLayerIndex;
790 DALI_LOG_RELEASE_INFO("layer is set to consume the hit\n");
791 results.renderTask = RenderTaskPtr(&renderTask);
792 results.actor = Dali::Layer(layers.GetLayer(consumedLayerIndex));
803 * Selects Prior Actor that is rendered later between firstActor and secondActor in the layer of rootActor.
804 * if only one of Actor is included in the layer, returns the Actor.
805 * if both of the firstActor and secondActor are not included in the layer, returns empty Actor.
807 Dali::Actor FindPriorActorInLayer(Dali::Actor rootActor, Dali::Actor firstActor, Dali::Actor secondActor)
809 Dali::Actor priorActor;
810 Dali::Layer layer = rootActor.GetLayer();
811 bool firstActorIncluded = firstActor.GetLayer() == layer;
812 bool secondActorIncluded = secondActor.GetLayer() == layer;
814 if(firstActorIncluded && !secondActorIncluded)
816 priorActor = firstActor;
818 else if(!firstActorIncluded && secondActorIncluded)
820 priorActor = secondActor;
822 else if(firstActorIncluded && secondActorIncluded)
824 priorActor = (GetImplementation(firstActor).GetSortingDepth() < GetImplementation(secondActor).GetSortingDepth()) ? secondActor : firstActor;
831 * Selects Prior Actor that is rendered later between firstActor and secondActor from child scene tree of rootActor.
833 Dali::Actor FindPriorActorInLayers(const LayerList& layers, Dali::Actor rootActor, Dali::Actor firstActor, Dali::Actor secondActor)
835 Dali::Layer sourceLayer = rootActor.GetLayer();
836 const uint32_t sourceActorDepth(sourceLayer.GetProperty<int>(Dali::Layer::Property::DEPTH));
838 Dali::Actor priorActor;
839 uint32_t layerCount = layers.GetLayerCount();
842 for(int32_t i = layerCount - 1; i >= 0; --i)
844 Layer* layer(layers.GetLayer(i));
845 if(sourceActorDepth == static_cast<uint32_t>(i))
847 priorActor = FindPriorActorInLayer(rootActor, firstActor, secondActor);
849 else if(IsWithinSourceActors(GetImplementation(rootActor), *layer))
851 Dali::Actor layerRoot = Dali::Actor(layer);
852 priorActor = FindPriorActorInLayer(layerRoot, firstActor, secondActor);
865 * Iterate through the RenderTaskList and perform hit testing.
867 * @param[in] sceneSize The scene size the tests will be performed in
868 * @param[in] layers The list of layers to test
869 * @param[in] taskList The list of render tasks
870 * @param[out] results Ray information calculated by the camera
871 * @param[in] hitCheck The hit testing interface object to use
872 * @param[in] propagationType Whether the scene using geometry event propagation touch and hover events.
873 * @return True if we have a hit, false otherwise
875 bool HitTestRenderTaskList(const Vector2& sceneSize,
877 RenderTaskList& taskList,
878 const Vector2& screenCoordinates,
880 HitTestInterface& hitCheck,
881 const Integration::Scene::TouchPropagationType propagationType)
883 if(propagationType == Integration::Scene::TouchPropagationType::GEOMETRY)
885 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
886 RenderTaskList::RenderTaskContainer::iterator endIter = tasks.end();
887 const auto& exclusives = taskList.GetExclusivesList();
890 // Hit test order should be of draw order
891 for(RenderTaskList::RenderTaskContainer::iterator iter = tasks.begin(); endIter != iter; ++iter)
893 RenderTask& renderTask = *iter->Get();
894 GeoHitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest);
897 return !results.actorLists.empty();
901 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
902 RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
903 const auto& exclusives = taskList.GetExclusivesList();
906 Results storedResults = results;
907 std::vector<std::pair<Dali::Actor, Results>> offScreenHitResults;
908 // Hit test order should be reverse of draw order
909 for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
911 RenderTask& renderTask = *iter->Get();
912 if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
914 if(renderTask.GetFrameBuffer())
916 Results result = results;
917 offScreenHitResults.push_back(std::make_pair(renderTask.GetScreenToFrameBufferMappingActor(), std::move(result)));
921 if(offScreenHitResults.empty())
926 Actor* sourceActor(renderTask.GetSourceActor());
927 for(auto&& pair : offScreenHitResults)
929 Dali::Actor mappingActor = pair.first;
930 if(!mappingActor || !IsWithinSourceActors(*sourceActor, GetImplementation(mappingActor)))
935 bool mappingActorInsideHitConsumingLayer = false;
936 if(GetImplementation(results.actor).IsLayer())
938 Dali::Layer resultLayer = Dali::Layer::DownCast(results.actor);
939 // Check the resultLayer is consuming hit even though the layer is not hittable.
940 // And check the resultLayer is the layer of mappingActor too.
941 if(hitCheck.DoesLayerConsumeHit(&GetImplementation(resultLayer)) && !hitCheck.IsActorHittable(&GetImplementation(results.actor)) && results.actor == mappingActor.GetLayer())
943 mappingActorInsideHitConsumingLayer = true;
946 if(mappingActorInsideHitConsumingLayer || mappingActor == FindPriorActorInLayers(layers, Dali::Actor(sourceActor), mappingActor, results.actor))
948 results = pair.second;
952 // Return true when an actor is hit (or layer in our render-task consumes the hit)
957 // When no OnScreen Actor is hitted but there are hit results from OffScreen RenderTasks
958 // those use ScreenToFrameBufferFunction, simply returns first hitted result.
959 if(!offScreenHitResults.empty())
961 results = offScreenHitResults.front().second;
965 results = storedResults;
971 * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
973 * @param[in] sceneSize The scene size the tests will be performed in
974 * @param[in] layers The list of layers to test
975 * @param[in] taskList The list of render tasks
976 * @param[out] results Ray information calculated by the camera
977 * @param[in] hitCheck The hit testing interface object to use
978 * @param[in] propagationType Whether the scene using geometry event propagation touch and hover events.
979 * @return True if we have a hit, false otherwise
981 bool HitTestForEachRenderTask(const Vector2& sceneSize,
983 RenderTaskList& taskList,
984 const Vector2& screenCoordinates,
986 HitTestInterface& hitCheck,
987 const Integration::Scene::TouchPropagationType propagationType)
991 if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, propagationType))
1000 } // unnamed namespace
1002 HitTestInterface::~HitTestInterface() = default;
1004 bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func, const Integration::Scene::TouchPropagationType propagationType)
1007 // Hit-test the regular on-scene actors
1008 Results hitTestResults;
1009 HitTestFunctionWrapper hitTestFunctionWrapper(func);
1010 if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper, propagationType))
1012 results.actor = hitTestResults.actor;
1013 results.actorCoordinates = hitTestResults.actorCoordinates;
1019 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface, const Integration::Scene::TouchPropagationType propagationType)
1023 // Hit-test the regular on-scene actors
1026 wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface, propagationType);
1031 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor, const Integration::Scene::TouchPropagationType propagationType)
1033 ActorTouchableCheck actorTouchableCheck;
1034 actorTouchableCheck.SetOwnActor(ownActor);
1035 return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck, propagationType);
1038 } // namespace Dali::Internal::HitTestAlgorithm