---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
---

+++ {"tags": ["locked"], "grade_id": "title"}

# Géométrie constructive des solides avec pyvista

+++ {"tags": ["locked"]}

:::{admonition} Objectifs pédagogiques
Dans cette feuille, nous allons découvrir comment **programmer** des objets 3D, par
exemple en vue de les imprimer au FabLab. On parle de géométrie constructive des solides.
Ce sera aussi l'occasion de mettre en œuvre les éléments de programmation rencontrés
précédemment.
:::

+++ {"tags": ["locked"]}

## Objets géométriques élémentaires

Commençons par construire une sphère et affichons la :

```{code-cell} ipython3
import pyvista
```

```{code-cell} ipython3
sphere = pyvista.Sphere()
sphere.plot()
```

+++ {"tags": ["locked"]}

Vérifiez que la sphere s'affiche bien !

+++ {"tags": ["locked"]}

Construisons maintenant une sphère en spécifiant son rayon (*radius*) et son centre
(*center*):

```{code-cell} ipython3
sphere = pyvista.Sphere(radius=3, center=(0,0,1))
sphere.plot()
```

+++ {"tags": ["locked"]}

:::{admonition} Exercice
1. Construisez de même un cube avec `pyvista.Cube`.
:::

```{code-cell} ipython3
:tags: [answer]

### BEGIN SOLUTION
cube = pyvista.Cube()
cube.plot()
### END SOLUTION
```

+++ {"tags": ["locked"]}

::::{admonition} Exercice (suite)
2. Construisez un parallélépipède rectangle de centre `(1,0,0)`, de longueur 3
   (`x_length`), de largeur 4 (`y_length`) et de hauteur 2 (`z_length`).
   :::{admonition} Indication
   :class: tip

   Consultez la documentation de `Cube` avec la commande `pyvista.Cube?`.
   :::
::::

```{code-cell} ipython3
:tags: [answer]

### BEGIN SOLUTION
cube = pyvista.Cube(center = (1,0,0), x_length=3, y_length=4, z_length=2)
cube.plot()
### END SOLUTION
```

+++ {"tags": ["locked"]}

:::{admonition} Exercice (fin)
1. Consultez la documentation de `pyvista.Cylinder` et `pyvista.Cone` pour construire
   respectivement un cylindre vertical et un cone vertical à 12 facettes.
2. Consultez la documentation de `pyvista.PlatonicSolid` pour d'autres objets.
:::

```{code-cell} ipython3
:tags: [answer]

### BEGIN SOLUTION
cylinder = pyvista.Cylinder(center=(1,0,0), direction=(0,0,1), height=10, radius=.5)
cylinder.plot()
### END SOLUTION
```

```{code-cell} ipython3
:tags: [answer]

### BEGIN SOLUTION
cone = pyvista.Cone(direction=(0,0,1), resolution=24)
cone.plot()
### END SOLUTION
```

+++ {"tags": ["locked"]}

## Des maillages, des couleurs et des scènes

+++ {"tags": ["locked"]}

Observez de près les objets que que nous avons construit ci-dessus. Vous constaterez que
ce sont des surfaces en trois dimensions obtenues en combinant une multitude de petites
surfaces plates, telles que des triangles. On les appelle des **maillages** (*mesh*).

Voici comment afficher un maillage en couleur :

```{code-cell} ipython3
sphere = pyvista.Sphere()
sphere.plot(color="red")
```

+++ {"tags": ["locked"]}

Pour afficher plusieurs maillages avec des couleurs différentes, il faut les insérer dans
une **scène**. Cette scène permettrait aussi de spécifier d'autres éléments, comme la
position par défaut de la caméra, etc.

```{code-cell} ipython3
cube = pyvista.Cube(center=(-1,0,0))
sphere = pyvista.Sphere(center=(1,0,0))

scene = pyvista.Plotter()
scene.add_mesh(cube, color='green')
scene.add_mesh(sphere, color='red')
scene.show()
```

+++ {"tags": ["locked"]}

## Construction de nouveaux objets par opérations booléennes

+++ {"tags": ["locked"]}

Nous allons maintenant construire de nouveaux objets en combinant le cube et la sphère.

:::{admonition} Astuce
:class: tip

`pyvista` construit la surface du cube à partir de carrés. Or, pour effectuer la plupart
des opérations entre solides à venir, nous avons besoin que la surface du solide soit
**triangulée**, c'est-à-dire définie par une collection de triangles. Pour cela on
triangule le cube ci-dessous.
:::

```{code-cell} ipython3
cube = pyvista.Cube().triangulate().subdivide(3)
sphere = pyvista.Sphere(radius=.65)
```

+++ {"tags": ["locked"]}

Calculons maintenant la différence entre le cube et la sphère et affichons la :

```{code-cell} ipython3
objet = cube.boolean_difference(sphere)
objet.plot(color="lightblue")
```

+++ {"tags": ["locked"]}

C'est un exemple d'**opération booléenne** entre figures géométriques. De la même
manière, on pourrait calculer la différence entre la sphère et le cube :

```{code-cell} ipython3
objet = sphere.boolean_difference(cube)
objet.plot(color="lightblue")
```

+++ {"tags": ["locked"]}

l'union entre le cube et la sphère :

```{code-cell} ipython3
objet = cube.boolean_union(sphere)
objet.plot(color="lightblue")
```

+++ {"tags": ["locked"]}

ou leur intersection :

```{code-cell} ipython3
objet = cube.boolean_intersection(sphere)
objet.plot(color="lightblue")
```

+++ {"tags": ["locked"]}

Pour l'union ou la différence, on peut utiliser les opérateurs `+` et `-` comme
ci-dessous:

```{code-cell} ipython3
objet = cube - sphere
objet.plot()
```

+++ {"tags": ["locked"]}

:::{admonition} Exercices
1. Construisez un bonhomme de neige en empilant une moyenne sphère sur une grande sphère.
2. Améliorez votre bonhomme de neige en rajoutant par exemple des yeux, un nez et tout ce
   que vous voudrez d'autre.
3. Construisez les anneaux olympiques, avec des anneaux à section carrée.
:::

## Transformations géométriques

Une fois un objet construit, il est possible de lui appliquer des transformations
géométriques: translations, rotations, homothéties.

Voici la réunion du cube et d'une copie translatée :

```{code-cell} ipython3
objet = cube + cube.translate([2,0,0])
objet.plot()
```

la réunion du cube et d'une rotation du cube de 45 degrés selon l'axe vertical :

```{code-cell} ipython3
objet = cube + cube.rotate_z(30)
objet.plot()
```

la réunion du cube et d'une copie translatée et de taille multipliée (*scale*) par `.5` :

```{code-cell} ipython3
objet = cube + cube.translate([2,0,0]).scale(.5)
objet.plot()
```

## Exercices

:::{admonition} Exercice
Reproduisez les
[exemples du tutoriel d'OpenScad](https://en.wikibooks.org/wiki/OpenSCAD_Tutorial/Chapter_1)
:::

+++

:::{admonition} Exercices : boucles et compréhensions
1. Disposez douze bonhommes de neige en cercle, regardant tous vers le centre.
2. Construisez un escalier.
3. Construisez un escalier en colimaçon.
:::

+++

:::{admonition} Exercices : récursivité
1. Construisez une
   [éponge de Menger](https://fr.wikipedia.org/wiki/%C3%89ponge_de_Menger).
2. Construisez des arbres.
:::

+++

## Extrusion

+++

L'[extrusion](https://fr.wikipedia.org/wiki/Extrusion#Dessin_vectoriel) est un procédé
puissant pour construire une forme tridimensionnelle à partir d'une forme plane, par
analogie à une extrusion mécanique. Pensez au boudin obtenu en pressant un tube de pâte
dentifrice, à travers son orifice rond.

:::{figure} https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Extrusions.jpg/500px-Extrusions.jpg
Objets virtuels créés par extrusion d’une même section suivant trois modes: rectiligne,
circulaire ou gauche (crédits: Wikipedia).
:::

:::{admonition} Voir aussi
:class: seealso

La [documentation d'OpenScad](https://en.wikibooks.org/wiki/OpenSCAD_Tutorial/Chapter_8).
:::

:::{todo} Ajouter un ou des exemples avec explications et des exercices
Par exemple tirés de la documentation d'OpenScad.
:::

+++

:::{admonition} Exercice
1. Consultez la documentation de
   [extrude](https://docs.pyvista.org/api/core/_autosummary/pyvista.polydatafilters.extrude)
   et
   [extrude_rotate](https://docs.pyvista.org/api/core/_autosummary/pyvista.polydatafilters.extrude_rotate).
2. Construisez des anneaux olympiques à section circulaire.
:::

+++

## Export et impression 3D

+++

Une fois que l'on a construit un objet, on peut le sauvegarder (*save*), par exemple dans
le format `stl` pour impression 3D:

```{code-cell} ipython3
:tags: [skip-execution]

objet.save("objet.stl")
```

Cela va créer un fichier `objet.stl` dans le même dossier que le carnet Jupyter. Vous
pouvez choisir n'importe quel autre nom.

### Impression avec Prusa

1. Téléchargez l'outil de Prusa depuis: https://www.prusa3d.com/page/prusaslicer_424/
2. Ouvrez le dossier où a été téléchargé le fichier.
3. Depuis le navigateur de fichier, désarchiver l'archive.
4. Faitez un clic droit sur le fichier
   `PrusaSlicer-2.8.1+linux-x64-newer-distros-GTK3-202409181416.AppImage` -> Propriétés
   -> Permissions -> Autoriser l'exécution du fichier.
5. Exécutez ce fichier.

### Impression avec Cura

1. Téléchargez la version Linux.AppImage de Cura depuis le
   [site web](https://ultimaker.com/software/ultimaker-cura/#downloads).

2. Ouvrez le dossier ou a été téléchargé le fichier

3. Faitez un clic droit sur le fichier -> Propriétés -> Permissions -> Autoriser
   l'exécution du fichier

4. Clic sur le fichier pour lancer l'application
