public static IList <FeatureHitTestResult> HitTestWithFeatures(this ARSCNView view, CGPoint point) { var results = new List <FeatureHitTestResult>(); var ray = view.HitTestRayFromScreenPosition(point); if (ray.HasValue) { var result = view.HitTestFromOrigin(ray.Value.Origin, ray.Value.Direction); if (result.HasValue) { results.Add(result.Value); } } return(results); }
public static SCNVector3?HitTestWithInfiniteHorizontalPlane(this ARSCNView view, CGPoint point, SCNVector3 pointOnPlane) { SCNVector3?result = null; var ray = view.HitTestRayFromScreenPosition(point); if (ray.HasValue) { // Do not intersect with planes above the camera or if the ray is almost parallel to the plane. if (ray.Value.Direction.Y <= -0.03f) { // Return the intersection of a ray from the camera through the screen position with a horizontal plane // at height (Y axis). result = Utilities.RayIntersectionWithHorizontalPlane(ray.Value.Origin, ray.Value.Direction, pointOnPlane.Y); } } return(result); }
public static IList <FeatureHitTestResult> HitTestWithFeatures(this ARSCNView view, CGPoint point, float coneOpeningAngleInDegrees, float minDistance = 0, float maxDistance = float.MaxValue, int maxResults = 1) { var results = new List <FeatureHitTestResult>(); ARPointCloud features = null; using (var frame = view.Session.CurrentFrame) { features = frame?.RawFeaturePoints; } if (features != null) { var ray = view.HitTestRayFromScreenPosition(point); if (ray.HasValue) { var maxAngleInDegrees = Math.Min(coneOpeningAngleInDegrees, 360f) / 2f; var maxAngle = (maxAngleInDegrees / 180f) * Math.PI; var points = features.Points; for (nuint j = 0; j < features.Count; j++) { var feature = points[j]; var featurePosition = new SCNVector3((Vector3)feature); var originToFeature = featurePosition - ray.Value.Origin; var crossProduct = SCNVector3.Cross(originToFeature, ray.Value.Direction); var featureDistanceFromResult = crossProduct.Length; var hitTestResult = ray.Value.Origin + (ray.Value.Direction * SCNVector3.Dot(ray.Value.Direction, originToFeature)); var hitTestResultDistance = (hitTestResult - ray.Value.Origin).Length; if (hitTestResultDistance < minDistance || hitTestResultDistance > maxDistance) { // Skip this feature - it is too close or too far away. continue; } var originToFeatureNormalized = SCNVector3.Normalize(originToFeature); var angleBetweenRayAndFeature = Math.Acos(SCNVector3.Dot(ray.Value.Direction, originToFeatureNormalized)); if (angleBetweenRayAndFeature > maxAngle) { // Skip this feature - is outside of the hit test cone. continue; } // All tests passed: Add the hit against this feature to the results. results.Add(new FeatureHitTestResult { Position = hitTestResult, DistanceToRayOrigin = hitTestResultDistance, FeatureHit = featurePosition, FeatureDistanceToHitResult = featureDistanceFromResult }); } // Sort the results by feature distance to the ray. results = results.OrderBy(result => result.DistanceToRayOrigin).ToList(); // Cap the list to maxResults. var cappedResults = new List <FeatureHitTestResult>(); var i = 0; while (i < maxResults && i < results.Count) { cappedResults.Add(results[i]); i += 1; } results = cappedResults; } } return(results); }