Ce document traite le sujet de centrale d’informatique de 2021.
import math, numpy as np
from typing import Tuple
vecteur = point = np.ndarray
rayon = Tuple[point, vecteur]
sphère = Tuple[point, float]
noir = np.array([0., 0., 0.])
blanc = np.array([1., 1., 1.])
couleur = image = np.ndarray
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
I Géométrie
Q 1.
def vec(A: point, B:point) -> vecteur:
return B - A
Q 2.
def ps(v1:vecteur, v2:vecteur) -> float:
return np.sum(v1 * v2)
Q 3.
def norme(v:vecteur) -> float:
return np.sqrt(ps(v, v))
Q 4.
def unitaire(v:vecteur) -> vecteur:
return (1. / norme(v)) * v
Q 5.
def pt(r:rayon, t:float) -> point:
"""Calcule les coordonées du point M du rayon r=(S, u) tel que SM = t*u"""
assert t >= 0
(S, u) = r
return S + t * u
def dir(A:point, B:point) -> vecteur:
"""Construit le vecteur unitaire associé à la direction du vecteur AB"""
return unitaire(vec(A, B))
def ra(A:point, B:point) -> rayon:
"""Construit le rayon lumineux issu du point A et de direction AB"""
return A, dir(A, B)
Q 6.
def sp(A:point, B:point) -> sphère:
return A, norme(vec(A, B))
Q 7.
Soit $M \in (A, \vec{u})$ alors $\exists t \in \mathbb{R}^+$ tel que $\vec{AM} = t \vec{u}$
Alors
i.e. $\forall M \in (A, \vec{u}) \,\, \exists t \in \mathbb{R}^+$ tel que $t^2 + 2t (\vec{u}\cdot\vec{CA}) + \lVert\vec{CA}\rVert^2 - \lVert\vec{CM}\rVert^2 = 0$ (1)
1) Supposons que $(A, \vec{u})$ et $(C, r)$ sont séquentes
L’intersection entre un cercle et une droite ne peut résulter qu’en un ou deux point que l’on note $M$ ou $M_1$ et $M_2$.
$M \in (C, r)$ implique que $\lVert\vec{CM}\rVert^2 = r^2$
donc d’après (1) on obtient : $t^2 + 2t (\vec{u}\cdot\vec{CA}) + \lVert\vec{CA}\rVert^2 - r^2 = 0$ et cette équation n’admet de solutions qu’en un ou deux points i.e. n’admet qu’une ou deux solutions réelles.
2) Supposons que $t^2 + 2t (\vec{u}\cdot\vec{CA}) + \lVert\vec{CA}\rVert^2 - r^2 = 0$ possède deux solutions réelles
Donc $\Delta = 4 (\vec{u}\cdot\vec{CA})^2 - 4 (\lVert\vec{CA}\rVert^2 - r^2) \ge 0$
et $t_1 = - \vec{u}\cdot\vec{CA} + \sqrt{(\vec{u}\cdot\vec{CA})^2 - (\lVert\vec{CA}\rVert^2 - r^2)}$
et $t_2 = - \vec{u}\cdot\vec{CA} - \sqrt{(\vec{u}\cdot\vec{CA})^2 - (\lVert\vec{CA}\rVert^2 - r^2)}$
sont les expressions des deux solutions de l’équation.
Soit $M_1 \in (A, \vec{u})$ tel que $\vec{AM_1} = t_1 \vec{u}$ et $M_2 \in (A, \vec{u})$ tel que $\vec{AM_2} = t_2 \vec{u}$
On a $\vec{AM_1} = \vec{AC} + \vec{CM_1} = t_1 \vec{u} = - (\vec{u}\cdot\vec{CA})\,\, \vec{u} + \sqrt{(\vec{u}\cdot\vec{CA})^2 - (\lVert\vec{CA}\rVert^2 - r^2)}\,\, \vec{u}$
i.e. $\vec{CM_1} = - (\vec{u}\cdot\vec{CA})\,\, \vec{u} + \sqrt{(\vec{u}\cdot\vec{CA})^2 - (\lVert\vec{CA}\rVert^2 - r^2)}\,\, \vec{u} + \vec{CA}$
donc $\vec{CM_1}\cdot\vec{u} = - \vec{u}\cdot\vec{CA} + \sqrt{(\vec{u}\cdot\vec{CA})^2 - (\lVert\vec{CA}\rVert^2 - r^2)} + \vec{CA}\cdot\vec{u} = \sqrt{(\vec{u}\cdot\vec{CA})^2 - (\lVert\vec{CA}\rVert^2 - r^2)}$
Soit $\vec{v}$ le vecteur unitaire orthogonal à $\vec{u}$ obtenu en lui applicant une rotation de $\frac{\pi}{2}$
alors $\vec{u}\cdot\vec{v} = 0$ et $\vec{u},\vec{v} = \frac{\pi}{2} \iff \vec{u},\vec{CA} + \vec{CA},\vec{v} = \frac{\pi}{2} \iff \vec{CA},\vec{v} = \frac{\pi}{2} - \vec{u},\vec{CA}$
donc $\vec{CM_1}\cdot\vec{v} = \vec{CA}\cdot\vec{v} = \lVert\vec{CA}\rVert \cos\left(\vec{CA},\vec{v}\right) = \lVert\vec{CA}\rVert \cos\left(\frac{\pi}{2} - \vec{u},\vec{CA}\right) = \lVert\vec{CA}\rVert \sin\left(\vec{u},\vec{CA}\right)$
ainsi
ainsi $M_1 \in (C, r)$, de même pour $M_2$ donc $(A, \vec{u})$ et $(C, r)$ sont séquentes en un ou deux points respectivement $M$ ou $M_1$ et $M_2$
Q 8.
def intersection(r:rayon, s:sphère) -> (point, float):
A, u = r
C, R = s
d = ps(u, vec(C, A))**2 - (norme(vec(C, A))**2 - R**2)
if (d < 0) or (norme(vec(C, A)) < R) or (ps(u, vec(C, A)) > 0):
# je le savais !!! il manque à l'énoncé la réciproque que c'est des solutions réelles positives pour que les solutions correspondent bien à une intersection du bon côté du rayon c.a.d côté direction de u
# donc il n'y a même pas besoin de cette longue preuve, la plus courte ou CM = .... = r marche !!
return np.array([-np.inf, -np.inf, -np.inf]), -np.inf
t = - ps(u, vec(C, A)) - np.sqrt(d)
return pt(r, t), t
Vérification
A = np.array([0., 0.5, 0.])
C = np.array([3., 0., 0.])
u = unitaire(vec(A, C + np.array([0., -0.5, 0.])))
r = 1.
M, t = intersection((A, u), (C, r))
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_aspect(1)
ax.set_xlim(-1, 5)
ax.set_ylim(-2, 2)
ax.add_artist(plt.Circle(C[:-1], r, fc='white', ec='green'))
ax.add_artist(plt.Line2D(*np.transpose([A, pt((A, u), 100)])[:-1]))
if (t >= 0):
ax.add_artist(plt.Circle(M[:-1], 0.05, fc='red', ec=None))
II Optique
II.A - Visibilité
Q 9.
Les directions des vecteurs $\vec{SP}$ et $\vec{CP}$ doivent être opposés i.e. $\vec{SP}\cdot\vec{CP} < 0$
Q 10.
def au_dessus(s:sphère, P:point, src:point) -> bool:
C = s[0]
S = src
return ps(dir(S, P), dir(C, P)) < 0
Q 11.
def visible(obj:[sphère], j:int, P:point, src:point) -> bool:
if not au_dessus(obj[j], P, src):
return False
for i, s in enumerate(obj):
if i != j:
if intersection(ra(P, src), s)[1] >= 0:
return False
return True
II.B - Diffusion
Q 12.
def couleur_diffusée(r:rayon, Cs:couleur, N:vecteur, kd:couleur) -> couleur:
A, u = r
return kd * Cs * ps(N, -u)
II.C - Réflexion
Q 13.
def pv(u:vecteur, v:vecteur) -> vecteur:
"""Produit vectoriel de u et v"""
return np.array([
u[1]*v[2] - u[2]*v[1],
u[2]*v[0] - u[0]*v[2],
u[0]*v[1] - u[1]*v[0]
])
def rotation(u:vecteur, N:vecteur, phi:float) -> vecteur:
"""Formule de rotation d'Euler - Rodrigues"""
return np.cos(phi) * u + (1 - np.cos(phi)) * ps(u, N) * N + np.sin(phi) * pv(N, u)
def rayon_réfléchi(s:sphère, P:point, src:point) -> rayon:
u = unitaire(vec(P, src))
C, r = s
N = unitaire(vec(C, P))
return P, rotation(u, N, np.pi)
Vérification
A = np.array([0., 0.5, 0.])
C = np.array([3., 0., 0.])
u = unitaire(vec(A, C + np.array([0., -0.5, 0.])))
r = 1.
fig, ax = plt.subplots(figsize=(10, 10))
ax.set_aspect(1)
ax.set_xlim(-1, 5)
ax.set_ylim(-2, 2)
# Cercle
ax.add_artist(plt.Circle(C[:-1], r, fc='white', ec='green'))
# Rayon
ax.plot(*np.transpose([A, pt((A, u), 100)])[:-1])
ax.arrow(*A[:2], *u[:2], head_width=.05, head_length=.07)
M, t = intersection((A, u), (C, r))
r = rayon_réfléchi((C, r), M, A)
A, u = r
ax.plot(*np.transpose([A, pt((A, u), 100)])[:-1])
ax.arrow(*A[:2], *u[:2], head_width=.05, head_length=.07)
N = unitaire(vec(C, M))
ax.arrow(*M[:2], *N[:2], head_width=.05, head_length=.07)
<matplotlib.patches.FancyArrow at 0x7ff85c5cfb10>
IV Lancer de rayons
IV.A - Écran
Q 18.
def grille(i:int, j:int) -> point:
return np.array([(j - N / 2) * Delta / N, (N / 2 - i) * Delta / N, 0.])
Q 19.
def rayon_écran(omega:point, i:int, j:int) -> rayon:
return (omega, unitaire(vec(omega, grille(i, j))))
IV.B - Couleur d’un pixel
Q 20.
def interception(r:rayon) -> (point, int):
tmin = np.inf
imin = -1
pmin = np.array([np.inf, np.inf, np.inf])
for i, s in enumerate(Objet):
M, t = intersection(r, s)
if 0 <= t < tmin:
tmin = t
imin = i
pmin = M
return pmin, imin
Q 21.
def couleur_diffusion(P:point, j:int) -> couleur:
c = noir
for i, src in enumerate(Source):
if visible(Objet, j, P, src):
r = (src, unitaire(vec(src, P)))
C = Objet[j][0]
N = unitaire(vec(C, P))
Cs = ColSrc[i]
kd = KdObj[j]
c = c + couleur_diffusée(r, Cs, N, kd)
return c
IV.C - Constitution de l’image
Q 22.
def lancer(omega:point, fond:couleur) -> image:
image = np.zeros((N, N, 3))
for i in range(N):
for j in range(N):
P, k = interception(rayon_écran(omega, i, j))
if k >= 0:
image[i][j] = couleur_diffusion(P, k)
else:
image[i][j] = fond
return image
IV.D - Compléxités
Q 23. Q 24.
Compléxité de interception
: $\mathcal{O}(n_o)$
Compléxité de visible
: $\mathcal{O}(n_o)$
Compléxité de couleur_diffusion
: $\mathcal{O}(n_on_s)$
Compléxité de lancer
: $\mathcal{O}(N^2n_on_s)$
Vérification
%%time
Objet = [
(np.array([2., 0., -7]), 3),
(np.array([-.7, 1.5, -2]), 1)
]
KdObj = [
np.array([1.0, 0.0, 0.0]),
np.array([0.0, 0.0, 1.0])
]
Source = [
np.array([-5, 5, 1]),
]
ColSrc = [
blanc,
]
Delta = 10
N = 500
omega = np.array([0., 0., 20])
gris_bleuté = np.array([0.8, 0.8, 0.8])
img = lancer(omega, gris_bleuté)
fig, ax = plt.subplots(figsize=(6, 6), dpi=120)
ax.imshow(img)
_ = plt.axis('off')
CPU times: user 24.7 s, sys: 41 ms, total: 24.8 s
Wall time: 24.8 s
%%time
Objet = [
(np.array([0., 0., -4]), 3),
]
KdObj = [
np.array([1.0, 0.0, 0.0]),
]
Source = [
np.array([-5, 5, -1]),
np.array([5, 5, -1]),
]
ColSrc = [
blanc,
blanc,
]
Delta = 10
N = 500
omega = np.array([0., 0., 20])
gris_bleuté = np.array([0.8, 0.8, 0.8])
img = lancer(omega, gris_bleuté)
fig, ax = plt.subplots(figsize=(6, 6), dpi=120)
ax.imshow(img)
_ = plt.axis('off')
CPU times: user 16.3 s, sys: 40.4 ms, total: 16.3 s
Wall time: 16.3 s
V Améliorations
V.A - Prise en compte de la réflexion
Q 25.
def réflexions(r:rayon, rmax:int) -> [(point, int)]:
pts = []
for k in range(rmax):
P, i = interception(r)
if i >= 0:
r = rayon_réfléchi(Objet[i], P, r[0])
pts.append((P, i))
return pts
Vérification
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
O = np.array([0, 0, 0])
x = np.array([1, 0, 0])
y = np.array([0, 1, 0])
z = np.array([0, 0, 1])
L = np.array([-5, 5, 1])
# base 3d
ax.scatter(*O, color='y')
ax.quiver(*O, *x, color='r')
ax.quiver(*O, *y, color='b')
ax.quiver(*O, *z, color='g')
# source
ax.scatter(*L, color='y')
# rayon
def dessine_rayon(r:rayon, ax):
A, u = r
ax.quiver(*A, *u)
ax.scatter(*A, color='g')
ax.plot3D(*np.transpose([A, pt((A, u), 100)]), lw=0.5)
C = np.array([ 2., 0., -7.])
r = 3.0
C1 = np.array([-1.3, 1.5, -2])
r1 = 1.0
u, v = np.mgrid[0:2*np.pi:20j, 0:np.pi:10j]
x = C[0] + r * np.cos(u)*np.sin(v)
y = C[1] + r * np.sin(u)*np.sin(v)
z = C[2] + r * np.cos(v)
ax.plot_wireframe(x, y, z, color="r", alpha=.2)
x = C1[0] + r1 * np.cos(u)*np.sin(v)
y = C1[1] + r1 * np.sin(u)*np.sin(v)
z = C1[2] + r1 * np.cos(v)
ax.plot_wireframe(x, y, z, color="b", alpha=.2)
ax.set_xlim([-5, 5])
ax.set_ylim([-5, 5])
ax.set_zlim([-10, 1])
def réflexions_test(r:rayon, rmax:int) -> [(point, int)]:
pts = []
for k in range(rmax):
P, i = interception(r)
if i >= 0:
r = rayon_réfléchi(Objet[i], P, r[0])
pts.append((r, i))
return pts
screenray = rayon_écran(omega, 240, 290)
refray = réflexions_test(screenray, 10)
dessine_rayon(screenray, ax)
for ray in refray:
dessine_rayon(ray[0], ax)
ax.view_init(20, 45)
Q 26.
def couleur_perçue(r:rayon, rmax:int, fond:couleur) -> couleur:
refx = réflexions(r, rmax)
Ck = noir
for P, k in reversed(refx):
if k >= 0:
Cdk = couleur_diffusion(P, k)
Ck = Cdk + KrObj[k] * Ck
return Ck
Q 27.
def lancer_complet(omega:point, fond:couleur, rmax:int) -> image:
image = np.zeros((N, N , 3))
for i in range(N):
for j in range(N):
r = rayon_écran(omega, i, j)
P, k = interception(r)
if k < 0:
image[i][j] = fond
else:
image[i][j] = couleur_perçue(r, rmax, fond)
return image
Vérification
%%time
Objet = [
(np.array([2., 0., -6.5]), 3),
(np.array([-2, 1.5, -1]), 2)
]
KdObj = [
np.array([0.8, 0.8, 0.8]),
np.array([0.0, 0.0, 1.0])
]
KrObj = [
1.0,
0.1
]
Source = [
np.array([4, 4, 1]),
]
ColSrc = [
blanc,
blanc,
blanc
]
Delta = 10
N = 500
omega = np.array([0., 0., 20])
gris_bleuté = np.array([0.8, 0.8, 0.8])
img = lancer_complet(omega, gris_bleuté, 3)
fig, ax = plt.subplots(figsize=(6, 6), dpi=120)
ax.imshow(img)
_ = plt.axis('off')
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
CPU times: user 42.5 s, sys: 76.1 ms, total: 42.6 s
Wall time: 42.5 s