Coder une application de mesure avec ARKit interaction et mesure

Avec de nombreuses autres choses qui ont rapidement été remplacées par notre technologie moderne, il semble que le ruban à mesurer commun puisse être le prochain à être utilisé. Dans cette série de didacticiels en deux parties, nous apprenons à utiliser la réalité augmentée et la caméra de votre appareil iOS pour créer une application permettant de signaler la distance entre deux points..

Dans le premier article, nous avons créé le projet d'application et codé ses principaux éléments d'interface. Dans cet article, nous terminerons en mesurant entre deux points de la scène AR. Si ce n'est déjà fait, suivez le premier message pour mettre en place votre projet ARKit..

Manipulation des robinets

Voici l'une des parties les plus importantes de ce didacticiel: la gestion lorsque l'utilisateur appuie sur son monde pour faire en sorte qu'une sphère apparaisse exactement à l'endroit où elle a tapé. Plus tard, nous calculerons la distance entre ces sphères pour finalement montrer à l'utilisateur leur distance.

Tap Gesture Recognizer

La première étape de la vérification des taps consiste à créer un outil de reconnaissance des gestes du tap lors du lancement de l'application. Pour ce faire, créez un gestionnaire de prises comme suit:

// Crée un gestionnaire de tap, puis le définit comme une constante, laissez tapRecognizer = UITapGestureRecognizer (target: self, action: #selector (handleTap))

La première ligne crée une instance du UITapGestureRecognizer () class et passe deux paramètres à l’initialisation: la cible et l’action. La cible est le destinataire des notifications envoyées par ce programme de reconnaissance, et nous voulons que notre ViewController classe pour être la cible. L'action est simplement une méthode qui doit être appelée à chaque fois qu'il y a un tap.

Pour définir le nombre de taps, ajoutez ceci:

// Définit le nombre de prises nécessaires pour déclencher le gestionnaire tapRecognizer.numberOfTapsRequired = 1

Ensuite, l'instance de la classe créée précédemment doit savoir combien de taps sont réellement nécessaires pour activer le module de reconnaissance. Dans notre cas, nous n'avons besoin que d'un robinet, mais dans d'autres applications, vous aurez peut-être besoin d'en avoir plus (comme un double tap) dans certains cas..

Ajoutez le gestionnaire à la vue de la scène comme ceci:

// Ajoute le gestionnaire à la vue de la scène sceneView.addGestureRecognizer (tapRecognizer)

Enfin, cette seule ligne de code n’ajoute que la reconnaissance de geste à la sceneView, c'est là que nous allons tout faire. C’est là que se trouve l’aperçu de la caméra et ce que l’utilisateur tapera directement pour faire apparaître une sphère à l’écran. Il est donc logique d’ajouter le dispositif de reconnaissance à la vue avec laquelle l’utilisateur va interagir..

Méthode du robinet

Quand nous avons créé le UITapGestureRecognizer (), vous vous en souvenez peut-être poignée méthode à l'action. Maintenant, nous sommes prêts à déclarer cette méthode. Pour ce faire, ajoutez simplement les éléments suivants à votre application:

@objc func handleTap (expéditeur: UITapGestureRecognizer) // Votre code va ici 

Bien que la déclaration de fonction puisse s’expliquer d'elle-même, vous pouvez vous demander pourquoi il existe un @objc tag devant. A partir de la version actuelle de Swift, vous avez besoin de cette balise pour exposer les méthodes à Objective-C. Tout ce que vous devez savoir c'est que #sélecteur a besoin que la méthode en question soit disponible pour Objective-C. Enfin, le paramètre de méthode nous permettra d'obtenir l'emplacement exact qui a été tapé sur l'écran.

Détection d'emplacement

La prochaine étape pour que nos sphères apparaissent à l'endroit où l'utilisateur a tapé consiste à détecter la position exacte qu'il a tapée. Maintenant, ce n’est pas aussi simple que d’obtenir l’emplacement et de placer une sphère, mais je suis sûr que vous allez le maîtriser très rapidement.. 

Commencez par ajouter les trois lignes de code suivantes à votre handleTap () méthode:

// Obtient l'emplacement du tap et l'assigne à une constante let location = sender.location (in: sceneView) // Recherche des objets du monde réel, tels que des surfaces, et filtre les surfaces planes. Let hitTest = sceneView.hitTest (location, types : [ARHitTestResult.ResultType.featurePoint]) // Affecte le résultat le plus précis à une constante s'il ne s'agit pas d'une protection non nulle. Let result = hitTest.last else return

Si vous vous souvenez du paramètre que nous avons pris dans le handleTap () méthode, vous vous souvenez peut-être qu'il a été nommé expéditeur, et c'était de type UITapGestureRecognizer. Eh bien, cette première ligne de code prend simplement la position du tap sur l’écran (par rapport à la vue de la scène) et la définit sur une constante nommée emplacement.

Ensuite, nous faisons quelque chose appelé un test de hit sur la SceneView lui-même. En termes simples, cela consiste à rechercher sur la scène des objets réels, tels que des tables, des surfaces, des murs, des sols, etc. Cela nous permet d’obtenir une idée de la profondeur et d’obtenir des mesures assez précises entre deux points. De plus, nous spécifions les types d’objets à détecter et, comme vous pouvez le constater, nous lui demandons de rechercher points caractéristiques, qui sont essentiellement des surfaces plates, ce qui est logique pour une application de mesure.

Enfin, la ligne de code prend le résultat le plus précis qui, dans le cas de hitTest est le dernier résultat, et vérifie si ce n'est pas néant. Si c'est le cas, il ignore le reste des lignes de cette méthode, mais s'il y a effectivement un résultat, il sera affecté à une constante appelée résultat.

Matrices

Si vous repensez à votre classe d'algèbre au lycée, vous vous souviendrez peut-être de matrices, qui semblaient peut-être moins importantes à l'époque qu'elles ne le sont à l'heure actuelle. Ils sont couramment utilisés dans les tâches d'infographie, et nous en aurons un aperçu dans cette application..

Ajoutez les lignes suivantes à votre handleTap () méthode, et nous allons les examiner en détail:

// Convertit le matrix_float4x4 en un SCNMatrix4 à utiliser avec SceneKit let transform = SCNMatrix4.init (result.worldTransform) // Crée un SCNVector3 avec certains index de la matrice let vector = SCNVector3Make (transform.m41, transform.m42, transform. m43) // Crée une nouvelle sphère avec la méthode créée, laissez sph = newSphere (at: vector)

Avant de passer à la première ligne de code, il est important de comprendre que le test d’attraction que nous avons effectué précédemment renvoie un type de matrix_float4x4, qui est essentiellement une matrice de quatre sur quatre des valeurs flottantes. Depuis que nous sommes enSceneKit, cependant, nous devrons le convertir en quelque chose que SceneKit peut comprendre - dans ce cas, en un SCNMatrix4.

Ensuite, nous utiliserons cette matrice pour créer un SCNVector3, qui, comme son nom l'indique, est un vecteur à trois composantes. Comme vous l'avez peut-être deviné, ces composants sont Xy, et z, pour nous donner une position dans l'espace. transformer.m41transformer.m42, et transformer.m43 sont les valeurs de coordonnées pertinentes pour les trois vecteurs composants.

Enfin, utilisons le newSphere () La méthode que nous avons créée précédemment, ainsi que les informations de localisation analysées à partir de l'événement tactile, permettent de créer une sphère et de l'affecter à une constante appelée sphère.

Résoudre le bug de double-tap

Maintenant, vous avez peut-être réalisé une légère faille dans notre code; si l'utilisateur continue à taper, une nouvelle sphère continuera à être créée. Nous ne voulons pas de cela, car il est difficile de déterminer quelles sphères doivent être mesurées. En outre, il est difficile pour l'utilisateur de garder une trace de toutes les sphères!

Résoudre avec des tableaux

La première étape pour résoudre ce problème consiste à créer un tableau en haut de la classe..

var sphères: [SCNNode] = []

Ceci est un tableau de SCNNodes parce que c'est le type que nous sommes revenus de notre newSphere () méthode que nous avons créée vers le début de ce tutoriel. Plus tard, nous mettrons les sphères dans ce tableau et vérifierons leur nombre. Sur cette base, nous pourrons manipuler leurs nombres en les supprimant et en les ajoutant.

Reliure optionnelle

Ensuite, nous utiliserons une série d'instructions if-else et des boucles for pour déterminer s'il existe ou non des sphères dans le tableau. Pour commencer, ajoutez la liaison facultative suivante à votre application:

if let first = spheres.first // Votre code va ici else // Votre code va ici

Tout d'abord, nous vérifions s'il y a tout articles dans le sphères tableau, et si non, exécutez le code dans le autre clause.

Auditer les sphères

Ensuite, ajoutez ce qui suit à la première partie (le si branche) de votre if-elsedéclaration:

// Ajoute une seconde sphère au tableau spheres.append (sphere) print (sphere.distance (to: first)) // Si plus de deux sont présents… si spheres.count> 2 // Parcourt le tableau de sphères pour une sphère dans les sphères // supprime toutes les sphères sphere.removeFromParentNode () // supprime les sphères superflues spheres = [sphères [2]] 

Comme nous sommes déjà dans un événement tap, nous savons que nous créons une autre sphère. Donc, s'il y a déjà une sphère, nous devons connaître la distance et l'afficher à l'utilisateur. Vous pouvez appeler le distance() méthode sur la sphère, car plus tard, nous allons créer une extension de SCNNode.

Ensuite, nous devons savoir s’il existe déjà plus que le maximum de deux sphères. Pour ce faire, nous utilisons simplement la propriété count de notre sphères tableau et un si déclaration. Nous parcourons toutes les sphères du tableau et les retirons de la scène. (Ne vous inquiétez pas, nous en reviendrons plus tard.)

Enfin, puisque nous sommes déjà dans le si déclaration qui nous dit qu'il y a plus de deux sphères, nous pouvons supprimer la troisième dans le tableau afin de nous assurer qu'il ne reste que deux sphères dans le tableau.

Ajout des sphères

Enfin, dans le autre clause, nous savons que la sphères Le tableau est vide, nous devons donc simplement ajouter la sphère que nous avons créée au moment de l'appel de la méthode. À l'intérieur de votre autre clause, ajouter ceci:

// Ajoute la sphère sphères.append (sphère)

Yay! Nous venons d'ajouter la sphère à notre sphères tableau, et notre tableau est prêt pour le prochain tap. Nous avons maintenant préparé notre tableau avec les sphères qui devraient apparaître à l'écran, alors maintenant, ajoutons-les simplement au tableau.

Pour parcourir et ajouter les sphères, ajoutez ce code:

// Itère dans un tableau de sphères pour une sphère dans des sphères // Ajoute toutes les sphères du tableau self.sceneView.scene.rootNode.addChildNode (sphere)

Ceci est juste un simple pour boucle, et nous ajoutons les sphères (SCNNode) En tant qu’enfant du nœud racine de la scène. Dans SceneKit, c’est le moyen privilégié d’ajouter des éléments..

Méthode complète

Voici ce que la finale handleTap () méthode devrait ressembler à:

@objc func handleTap (expéditeur: UITapGestureRecognizer) let emplacement = sender.location (dans: sceneView) let hitTest = sceneView.hitTest (emplacement, types: [ARHitTestResult.ResultType.featurePoint]) guard let result = hitTest.last else retour  let transform = SCNMatrix4.init (result.worldTransform) let vector = SCNVector3Make (transform.m41, transform.m42, transform.m43) let sphere = newSphere (à: vecteur) si let = spheres.first spheres.append ( sphere) print (sphere.distance (to: first)) si spheres.count> 2 pour une sphère dans les sphères sphere.removeFromParentNode () spheres = [sphères [2]] spheres.append (sphère) pour une sphère dans des sphères self.sceneView.scene.rootNode.addChildNode (sphere)

Calculer les distances

Maintenant, si vous vous en souvenez, nous avons appelé un distance (à :) méthode sur notre SCNNode, la sphère, et je suis sûr que Xcode vous crie d’utiliser une méthode non déclarée. Mettons fin à cela maintenant, en créant une extension de la SCNNode classe.

Pour créer une extension, procédez comme suit en dehors de votre ViewController classe:

extension SCNNode // Votre code va ici

Cela vous permet simplement de modifier la classe (c'est comme si vous éditiez la classe réelle). Ensuite, nous allons ajouter une méthode qui calculera la distance entre deux nœuds.

Voici la déclaration de la fonction pour le faire:

func distance (vers la destination: SCNNode) -> CGFloat // Votre code va ici

Si vous voyez, il y a un paramètre qui est un autre SCNNode, et il retourne un CGFloat comme résultat. Pour le calcul réel, ajoutez ceci à votre distance() une fonction:

let dx = destination.position.x - position.x let dy = destination.position.y - position.y let dz = destination.position.z - position.z let pouces: Flotteur = 39.3701 let metres = sqrt (dx * dx + dy * dy + dz * dz) return CGFloat (mètres * pouces)

Les trois premières lignes de code soustraient les positions x, y et z du courant SCNNode à partir des coordonnées du noeud passé en paramètre. Nous intégrerons ensuite ces valeurs dans la formule de distance pour obtenir leur distance. De plus, parce que je veux le résultat en pouces, j'ai créé une constante pour le taux de conversion entre mètres et pouces pour une conversion facile plus tard.. 

Maintenant, pour obtenir la distance entre les deux nœuds, repensez-vous à votre cours de mathématiques au collège: vous vous souvenez peut-être de la formule de distance pour le plan cartésien. Ici, nous l'appliquons à des points dans un espace tridimensionnel.

Enfin, nous renvoyons la valeur multipliée par le ratio de conversion en pouces pour obtenir l'unité de mesure appropriée. Si vous habitez en dehors des États-Unis, vous pouvez le laisser en mètres ou le convertir en centimètres si vous le souhaitez..

Conclusion

Eh bien, c'est un wrap! Voici à quoi devrait ressembler votre projet final:

Comme vous pouvez le constater, les mesures ne sont pas parfaites, mais un ordinateur de 15 pouces pèse environ 14,998 pouces. Ce n'est donc pas mauvais.!

Vous savez maintenant comment mesurer les distances à l'aide de la nouvelle bibliothèque d'Apple, ARKit. Cette application peut être utilisée pour beaucoup de choses, et je vous mets au défi de penser à différentes manières de l'utiliser dans le monde réel, et de laisser vos pensées dans les commentaires ci-dessous..

Assurez-vous également de consulter le dépôt GitHub pour cette application. Et tant que vous êtes toujours là, consultez nos autres tutoriels de développement iOS ici sur Envato Tuts.+!