Aplicatii ale geometriei în informatică - Laboratoare

Lab 1

În lista de probleme de mai jos, ΔABC are vârfurile A(1,1), B(4,1), C(2,3).

Problema 2

Determinați imaginea triunghiului ABC printr-o scalare uniformă de factor de scală 2 relativ la punctul Q(2,2).

Soluție

Forma generală a matricei de scalare în jurul unui punct Q:

SQ(sx,sy)=(sx0(1sx)xQ0sy(1sy)yQ001)

Concret, Q=(2,2),sx=sy=2. Matricea transformării este:

T=SQ(2,2)=(202022001)

Imaginea ΔABC după scalare este:

[ABC]=T[ABC]
=(202022001)(142113111)=(062004111)

Rezultă A(0,0), B(6,0), C(2,4).

Problema 4

Determinați imaginea triunghiului ABC printr-o forfecare de unghi 45, relativ la punctul Q(2,2), în direcția vectorului v(2,1).

Soluție

Forma generală a matricei de forfecare Q, de unghi θ, în direcția versorului v¯, este

Shear(Q,v¯,θ)=(1tanθv1v2tanθv12tanθv1(v1Q2v2Q1)tanθv221+tanθv1v2tanθv2(v1Q2v2Q1)001)

În particular, Q=(2,2), v¯=(v1|v|,v2|v|)=(25,15),tanθ=1

Matricea transformării este:

T=Shear(Q,v¯,45)=(354545157525001)

Imaginea ΔABC după forfecare este:

[ABC]=T[ABC]
=15(344172005)(142113111)=(351251454515175111)

Rezultă A(35,45), B(125,15), C(145,175)

Problema 5

Aplicați forfecarea de la problema precedentă pătratului ABCD, cu A(0,0), B(3,0), C(3,3) și D(0,3).

Soluție

Aplicăm transformarea T găsită la problema 4 vârfurilor pătratului ABCD:

[ABCD]=T[ABCD]
=[451175852511651951111]

Obținem A(45,25), B(1,1), C(175,165), D(85,195)

Problema 7

Determinați imaginea triunghiului ABC prin reflexia relativ la dreapta 2x+3y5=0.

Soluție

Forma generală a matricei de reflexie față de o dreaptă Δ:ax+by+c=0 este:

RΔ=(b2a2a2+b22aba2+b22aca2+b22aba2+b2b2a2a2+b22bca2+b2001)

În particular, pentru dreapta Δ:2x+3y5=0, matricea transformării este:

T=RΔ=(5131213201320135133013001)

Imaginea ΔABC după transformare este:

[ABC]=T[ABC]
=(1281361312313913111)

Obținem A(1,1), B(2813,2313), C(613,913)

Problema 10

Determinați imaginea triunghiului ABC prin rotația cu 90 în jurul punctului C, urmată de reflexia relativ la dreapta AB.

Soluție

Forma generală a matricei de rotație în jurul unui punct P cu unghi θ:

RotP(θ)=(cosθsinθxP(1cosθ)+yPsinθsinθcosθxPsinθ+yP(1cosθ)001)

Calculăm imaginea ΔABC după rotația cu 90 în jurul punctului C:

T=RotC(90)=(015101001)
[ABC]=T[ABC]=(442253111)

Rezultă A(4,2), B(4,5), C(2,3).

Forma generală a matricei de reflexie față de o dreaptă Δ:ax+c=0 este

RΔ=(102ca010001)

În cazul nostru, Δ=AB, a=1, c=4, de unde matricea de transformare este:

T=RΔ=(108010001)

Imaginea finală a ΔABC după transformări este

[A10B10C10]=T[ABC]=(446253111)
Obținem triunghiul ΔA10B10C10, A10(4,2), B10(4,5), C10(6,3)

Lab 2 Info

Scrieți un program în Python care să determine imaginea unui poliedru printr-o reflexie față de un plan dat. Considerați atât planul dat prin ecuația generală (datele de intrare vor fi coeficienții ecuației), cât și planul dat printr-un punct și vectorul normal. În cel de-al doilea caz, scrieți mai întâi ecuația generala, plecând de la punct și vectorul normal. Programul trebuie să conțină următorii pași:

  1. determinarea punctului de intersecție dintre plan și una dintre axe;
  2. determinarea matricii translației care face planul sa treacă prin origine;
  3. determinerea matricii rotației față de o axă care face ca normala la plan sâ fie paralelă cu un plan de coordonate;
  4. determinarea matricii rotației față de altă axă care face ca planul să coincidă cu un plan de coordonate;
  5. determinarea matricii reflexiei față de planul de la punctul precedent;
  6. determinarea matricii rotației inverse rotației de la punctul 4;
  7. determinarea matricii rotației inverse celei de la punctul 3;
  8. determinarea matricii translației inverse celei de la punctul 2;
  9. determinarea matricii transformării, prin înmulțirea celor 7 matrici precedente.
  10. citirea vârfurilor poliedrului;
  11. stabilirea matricii omogene a coordonatelor vârfurilor poliedrului transformat.

Soluție

Definim o clasă pentru matricea omogenă în 3D:

In [14]:
from __future__ import annotations
from math import sqrt, cos, sin


class Matrix4:
    def __init__(self, *args):
        self.items = [0]*16
        for i in range(min(16, len(args))):
            self.items[i]=args[i]

    def __getitem__(self, pos):
        i, j = pos
        return self.items[4 * i + j]

    def __setitem__(self, pos, value):
        i, j=pos
        self.items[4 * i + j]=value

    def __str__(self):
        s=""
        for i in range(4):
            s += str("".join(list(map(lambda x:f"{x:.3f}".rjust(8), self.items[4*i:4*i+4]))))+"\n"
        return s

    def _scalar_mul_(self, x:float) -> Matrix4: return Matrix4([x*i for i in self.items])

    def _matrix4_mul_(self, m:Matrix4) -> Matrix4:
        r = Matrix4()
        for i in range(4):
            for j in range(4):
                for k in range(4):
                    r[i,j]+=self[i,k]*m[k,j]
        return r

    def _vec4_mul(self, v:tuple[float]):
        r = [0]*4
        v = (v[0], v[1], v[2], 1)
        for i in range(4):
            for j in range(4):
                r[i] += self[i,j]*v[j]
        return tuple(r)

    def __mul__(self, other):
        if isinstance(other, float) or isinstance(other, int): return self._scalar_mul_(other)
        if isinstance(other, Matrix4): return self._matrix4_mul_(other)
        if isinstance(other, tuple): return self._vec4_mul(other)
        raise Exception("Unsupported operation: invalid multiplication type")

    def __rmul__(self, other):
        if isinstance(other, float) or isinstance(other, int): return self._scalar_mul_(other)
        if isinstance(other, Matrix4): return other._matrix4_mul_(self)
        raise Exception("Unsupported operation: invalid multiplication type")

    def apply(self, pts): return [self * p for p in pts]

Definim principalele tipuri de matrici de transformare:

In [4]:
def TranslationMatrix(p):
    x, y, z = p
    return Matrix4(1,0,0,x, 0,1,0,y, 0,0,1,z, 0,0,0,1)

def CosSinRotXMatrix(c,s): return Matrix4(1,0,0,0, 0,c,-s,0, 0,s,c,0, 0,0,0,1)
def RotationXMatrix(thetaX): return CosSinRotXMatrix(cos(thetaX), sin(thetaX))

def CosSinRotYMatrix(c,s): return Matrix4(c,0,s,0, 0,1,0,0, -s,0,c,0, 0,0,0,1)
def RotationYMatrix(thetaY): return CosSinRotYMatrix(cos(thetaY), sin(thetaY))

def CosSinRotZMatrix(c,s): return Matrix4(c,-s,0,0, s,c,0,0, 0,0,1,0, 0,0,0,1)
def RotationZMatrix(thetaZ): return CosSinRotZMatrix(cos(thetaZ), sin(thetaZ))

def ReflectYZMatrix(): return Matrix4(-1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1)

Câteva metode ajutătoare:

In [5]:
"""
    in p: planul (a,b,c,d)
    out: (point, v), point = punctul de intersectie, v = versor Ox, Oy sau Oz
"""
def intersectAxis(p):
    a,b,c,d = p
    if a!=0: return ((-d/a,0,0), (1,0,0))
    if b!=0: return ((0,-d/b,0), (0,1,0))
    if c!=0: return ((0,0,-d/c), (0,0,1))
    if d!=0: raise Exception("Runtime error: plan invalid d=0, d!=0")
    return ((0, 0, 0), (1, 0, 0)) # a=b=c=0, d=0 triat la input

def neg(p):
    x,y,z = p
    return (-x,-y,-z)

Definirea planului ax+by+cz+d=0 (a se vedea LiviuStefan_NeacsuMiclea_235_Lab2Info.py pentru citirea planului de la tastatura, in ambele variante precizate de cerinta):

In [6]:
plane = (2,1,3,-10) 
a,b,c,d = plane

Calcularea matricilor de transformare, conform pașilor precizați:

In [15]:
transforms = []
inv_transforms = []

print("Calculam intersectia cu una dintre axe")
i_point, i_axis = intersectAxis(plane)
print(f"Planul intersecteaza axa data de versorul {i_axis} in punctul {i_point}")

print("Determinam matricea de translatie care face planul sa treaca prin origine:")

if d!=0:
    translate_o = TranslationMatrix(neg(i_point))
    print(translate_o)
    transforms.append(translate_o)
    inv_transforms.append(TranslationMatrix(i_point))
else:
    print("Planul este deja in origine")

print("Determinam matricea de rotatie fata de o axa care face ca normala \n"
      "la plan sa fie paralela cu un plan de coordonate:")
print(">> Rotatie in jurul axei Ox pentru a face normala || cu planul xOy aka perp. pe axa Oz")

if c!=0:
    norm_x = sqrt(b**2+c**2)
    cos_tx = b/norm_x
    sin_tx = -c/norm_x
    rot_x = CosSinRotXMatrix(cos_tx, sin_tx)
    print(rot_x)
    transforms.append(rot_x)
    inv_transforms.append(CosSinRotXMatrix(cos_tx, -sin_tx))
else:
    rot_x = RotationXMatrix(0)
    print("Normala este deja paralela cu axa Oz")

print("Determinam matricea de rotatie fata de alta axa care face ca planul \n"
      "sa coincida cu un plan de coordonate")
print(">> Rotatie in jurul axei Oz pentru a face planul || cu planul yOz")

new_a, new_b,_,_ = rot_x.apply([(a,b,c)])[0]

if new_b != 0:
    norm_z = sqrt(new_a**2+new_b**2)
    cos_tz = new_a/norm_z
    sin_tz = -new_b/norm_z
    rot_z = CosSinRotZMatrix(cos_tz, sin_tz)
    print(rot_z)
    transforms.append(rot_z)
    inv_transforms.append(CosSinRotZMatrix(cos_tz, -sin_tz))
else:
    print("Planul este deja paralel cu axa Oz")


print("Planul se transforma in planul yOz")
print("Matricea de reflexie fata de YZ:")

refl_yz = ReflectYZMatrix()
print(refl_yz)
transforms.append(refl_yz)

print("Matricile inverse transformarilor planului (in ordinea inversa mentionarii lor)")

for m in inv_transforms[::-1]:
    print(str(m)+"\n")

transforms = (transforms+ inv_transforms[::-1])[::-1]

full_t = Matrix4(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1)
for t in transforms:
    full_t *= t

print("Matricea transformarii compuse")
print(full_t)

print()
Calculam intersectia cu una dintre axe
Planul intersecteaza axa data de versorul (1, 0, 0) in punctul (5.0, 0, 0)
Determinam matricea de translatie care face planul sa treaca prin origine:
   1.000   0.000   0.000  -5.000
   0.000   1.000   0.000   0.000
   0.000   0.000   1.000   0.000
   0.000   0.000   0.000   1.000

Determinam matricea de rotatie fata de o axa care face ca normala 
la plan sa fie paralela cu un plan de coordonate:
>> Rotatie in jurul axei Ox pentru a face normala || cu planul xOy aka perp. pe axa Oz
   1.000   0.000   0.000   0.000
   0.000   0.316   0.949   0.000
   0.000  -0.949   0.316   0.000
   0.000   0.000   0.000   1.000

Determinam matricea de rotatie fata de alta axa care face ca planul 
sa coincida cu un plan de coordonate
>> Rotatie in jurul axei Oz pentru a face planul || cu planul yOz
   0.535   0.845   0.000   0.000
  -0.845   0.535   0.000   0.000
   0.000   0.000   1.000   0.000
   0.000   0.000   0.000   1.000

Planul se transforma in planul yOz
Matricea de reflexie fata de YZ:
  -1.000   0.000   0.000   0.000
   0.000   1.000   0.000   0.000
   0.000   0.000   1.000   0.000
   0.000   0.000   0.000   1.000

Matricile inverse transformarilor planului (in ordinea inversa mentionarii lor)
   0.535  -0.845   0.000   0.000
   0.845   0.535   0.000   0.000
   0.000   0.000   1.000   0.000
   0.000   0.000   0.000   1.000


   1.000   0.000   0.000   0.000
   0.000   0.316  -0.949   0.000
   0.000   0.949   0.316   0.000
   0.000   0.000   0.000   1.000


   1.000   0.000   0.000   5.000
   0.000   1.000   0.000   0.000
   0.000   0.000   1.000   0.000
   0.000   0.000   0.000   1.000


Matricea transformarii compuse
   0.429  -0.286  -0.857   2.857
  -0.286   0.857  -0.429   1.429
  -0.857  -0.429  -0.286   4.286
   0.000   0.000   0.000   1.000


Testarea transformării pe un poliedru generat care are vârfurile pe sfera unitate:

In [31]:
def gen_sphere(p, r):
    pts = []
    x,y,z=p
    for t in range(0, 4):
        at = 6.28 * t/4
        for p in range(0, 4):
            ap = 3.14 * p/4
            xx = x+r*cos(at)*cos(ap)
            yy = y + r * cos(at) * sin(ap)
            zz = z + r * sin(at)
            pts.append((xx,yy,zz))
    return pts

pts = gen_sphere((0, 0, 0), 1)

print("Matricea coordonatelor ale poliedrului initial")
for j in range(3):
    for i in range(len(pts)):
        print(f"{pts[i][j]:.2f}".rjust(5,' '), end=" ")
    print("")
for i in range(len(pts)):
    print(f"{1}".rjust(5, ' '), end=" ")
print("\n")
Matricea coordonatelor ale poliedrului initial
 1.00  0.71  0.00 -0.71  0.00  0.00  0.00 -0.00 -1.00 -0.71 -0.00  0.71 -0.00 -0.00 -0.00  0.00 
 0.00  0.71  1.00  0.71  0.00  0.00  0.00  0.00  0.00 -0.71 -1.00 -0.71  0.00 -0.00 -0.00 -0.00 
 0.00  0.00  0.00  0.00  1.00  1.00  1.00  1.00  0.00  0.00  0.00  0.00 -1.00 -1.00 -1.00 -1.00 
    1     1     1     1     1     1     1     1     1     1     1     1     1     1     1     1 

In [32]:
pts_t = full_t.apply(pts)
print("Matricea coordonatelor poliedrului transformat")
for j in range(3):
    for i in range(len(pts_t)):
        print(f"{pts_t[i][j]:.2f}".rjust(5,' '), end=" ")
    print("")
for i in range(len(pts_t)):
    print(f"{1}".rjust(5, ' '), end=" ")
print("\n")
Matricea coordonatelor poliedrului transformat
 3.29  2.96  2.57  2.35  2.00  2.00  2.00  2.00  2.43  2.75  3.14  3.36  3.71  3.71  3.71  3.72 
 1.14  1.83  2.29  2.24  1.00  1.00  1.00  1.00  1.71  1.02  0.57  0.62  1.86  1.86  1.86  1.86 
 3.43  3.38  3.86  4.59  4.00  4.00  4.00  4.00  5.14  5.19  4.71  3.98  4.57  4.57  4.57  4.57 
    1     1     1     1     1     1     1     1     1     1     1     1     1     1     1     1 

Reprezentarea grafică a transformării:

In [33]:
import matplotlib.pyplot as plt
import numpy as np

def plot_axes(ax):
    x, y, z = np.zeros((3, 1))
    u, v, w = np.array([5, 0, 0])
    ax.quiver(x, y, z, u, v, w, arrow_length_ratio=0.1, color='r')
    u, v, w = np.array([0, 5, 0])
    ax.quiver(x, y, z, u, v, w, arrow_length_ratio=0.1, color='g')
    u, v, w = np.array([0, 0, 5])
    ax.quiver(x, y, z, u, v, w, arrow_length_ratio=0.1, color='b')


def plot_points(ax, pts, m="o", c="r"):
    for p in pts:
        ax.scatter(p[0], p[1], p[2], marker=m, color=c)


def plot_surface(ax, pts):
    x = np.array([p[0] for p in pts])
    y = np.array([p[1] for p in pts])
    z = np.array([p[2] for p in pts])
    ax.scatter(x, y, z)


def plot_plane(fig, p):
    a, b, c, d = p
    if c != 0:
        x = np.linspace(-5, 5, 10)
        y = np.linspace(-5, 5, 10)
        X, Y = np.meshgrid(x, y)
        Z = (-d - a * X - b * Y) / c
    elif b != 0:
        x = np.linspace(-5, 5, 10)
        z = np.linspace(-5, 5, 10)
        X, Z = np.meshgrid(x, z)
        Y = (-d - a * X) / b
    elif a != 0:
        y = np.linspace(-5, 5, 10)
        z = np.linspace(-5, 5, 10)
        Y, Z = np.meshgrid(y, z)
        X = (-d - 0 * Y - 0 * Z) / a
    else:
        y = np.linspace(-5, 5, 10)
        z = np.linspace(-5, 5, 10)
        Y, Z = np.meshgrid(y, z)
        X = (0 - 0 * Y - 0 * Z)

    ax.plot_surface(X, Y, Z)
In [42]:
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
plot_plane(ax, plane)

plot_points(ax, pts, c="r")
plot_points(ax, pts_t, c="b")

plot_axes(ax)
ax.view_init(-10, -30)
plt.show()

Lab 3 Info

Problema 1

Consideram curba Bézier cubică dată de punctele de control b0(3,1), b1(4,4), b2(4,4), b3(3,1).

  1. Determinați punctele de control ale curbei, privită ca o curbă de gradul 4.
  2. Reprezentați grafic pe același sistem de axe: curba, punctele de control și poligonul de control ale curbei privite ca o curbă de gradul 3, precum și punctele de control și poligonul de control ale curbei, privite ca o curbă de gradul 4.

Soluție

1.Pentru curba Bézier de gradul n dată de punctele de control b0,,bn, se poate construi curba identică de grad n+1, dată de punctele de control c0,,cn+1, astfel:

c0=b0ck=n+1kn+1bk+kn+1bk+1,k=1,,ncn+1=bn

În particular, pentru n=3, b0(3,1), b1(4,4), b2(4,4), b3(3,1), găsim:

c0=(3,1)c1=(154,134)c2=(0,4)c3=(154,134)c4=(3,1)

2.Reprezentare grafică:

Problema 2

Considerăm curba Bézier de gradul 3 de la problema precedentă.

  1. Divizați curba la valoarea 1/3 a parametrului, determinând punctele de control și parametrizările pe intervalul [0,1] pentru fiecare dintre cele două segmente.
  2. Reprezentați grafic pe același sistem de axe: cele doua segmente de curbă (cu culori diferite), punctele de control și poligonul de control pentru curba originală, precum și punctele de control și poligoanele de control pentru cele doua segmente obținute prin divizare.

Soluție

1.Pentru curba Bézier dată de punctele de control b0,,bn și un interval [a,b][0,1], curba Bézier care descrie curba segmentată pe intervalul [a,b] are punctele de control

ck=C(a)nkC(b)k(b0bn)
Aplicăm pentru n=3 și b0(3,1), b1(4,4), b2(4,4), b3(3,1), cu intervalele [a,b]=[0,1/3], respectiv [a,b]=[1/3,1].

Rescriem ecuațiile lui ck din teoremă particularizate pentru n=3:

c0=C(a)3C(b)0(b0b1b2b3)=C1(a)C2(a)C3(a)(b0b1b2b3)=[1aa][1aa001aa][1aa0001aa0001aa](b0b1b2b3)c1=C(a)2C(b)1(b0b1b2b3)=C1(a)C2(a)C3(b)(b0b1b2b3)=[1aa][1aa001aa][1bb0001bb0001bb](b0b1b2b3)c2=C(a)1C(b)2(b0b1b2b3)=C1(a)C2(b)C3(b)(b0b1b2b3)=[1aa][1bb001bb][1bb0001bb0001bb](b0b1b2b3)c3=C(b)1C(b)2(b0b1b2b3)=C1(b)C2(b)C3(b)(b0b1b2b3)=[1bb][1bb001bb][1bb0001bb0001bb](b0b1b2b3)

Efectuând calulele obținem:

  • pentru [a,b]=[0,1/3]: c0(3,1), c1(103,2), c2(83,83), c3(53,3)
  • pentru [a,b]=[1/3,1]: c0(53,3), c1(13,113), c2(113,3), c3(3,1)

2.Reprezentare grafică:

Lab 4 Info

Problemă

Scrieți un program (în Python) care să determine un punct de pe o curbă B-spline cubică, în plan, cu 5 puncte de control, folosind algoritmul lui de Boor. Datele de intrare vor fi:

  • cele 5 puncte de control;
  • cele 9 noduri;
  • un t[t3,t5],

iar rezultatul trebuie sa fie r(t).

Soluție

Definim o clasă pentru un punct în plan:

In [64]:
from future import __annotations__

class Point:
    def __init__(self,tuplexy):
        self.x=tuplexy[0]
        self.y=tuplexy[1]

    def __mul__(self, f:float):
        return Point((self.x*f, self.y*f))

    def __rmul__(self, f:float):
        return Point((self.x*f, self.y*f))

    def __add__(self, pt):
        return Point((self.x+pt.x, self.y+pt.y))

    def __str__(self):
        return f"({self.x:.3f}, {self.y:.3f})"

Algoritmul lui de Boor:

In [65]:
def de_boor(d,t,tx):
    n = 3
    N = 2
    r = 0
    for i in range(len(t)-1):
        if t[i] <= tx < t[i + 1]:
            r = i

    assert(n <= r)
    assert (r < n + N)

    dk = [Point((0,0))]*len(d)
    a = [0]*len(d)

    for i in range(r - n, r + 1): dk[i] = d[i]    

    for k in range(1,n+1):
        ndk = [Point((0,0))] * len(d)
        for i in range(r-n+k,r+1):
            a[i] = (tx-t[i])/(t[n+1+i-k]-t[i])
            ndk[i] = (1-a[i])*dk[i-1]+a[i]*dk[i]
        dk = ndk        
    return dk[r]

Datele de intrare (pentru exemplificare):

In [66]:
d = list(map(Point, [(-1, 17), (1, 2), (3, 4), (5, 6), (7, -8)]))
t = [0, 0, 0, 0, 0.5, 1, 1, 1, 1]

Lista punctelor în care vrem să calculăm valoarea curbei:

In [67]:
txs = [0, 0.2, 0.5, 0.7, 0.9]
In [69]:
print("Punctele de control:")
print(", ".join(list(map(str,d))))
print("Nodurile:")
print(t)
Punctele de control:
(-1.000, 17.000), (1.000, 2.000), (3.000, 4.000), (5.000, 6.000), (7.000, -8.000)
Nodurile:
[0, 0, 0, 0, 0.5, 1, 1, 1, 1]

Calculul valorilor r(t):

In [73]:
print("Valorile curbei in punctele date:")
for tx in txs:
    p = de_boor(d,t,tx)
    print(f"r({str(tx).rjust(3)}) = {p}")
Valorile curbei in punctele date:
r(  0) = (-1.000, 17.000)
r(0.2) = (0.984, 5.656)
r(0.5) = (3.000, 4.000)
r(0.7) = (4.264, 4.240)
r(0.9) = (5.912, -1.280)

Reprezentarea grafică:

In [75]:
pts = [de_boor(d, t, tx * 0.01) for tx in range(int(100*min(t)), int(100*max(t)))]
xs = [p.x for p in pts]
ys = [p.y for p in pts]
plt.plot(xs, ys)
pts = [de_boor(d, t, tx) for tx in txs]
xs = [p.x for p in pts]
ys = [p.y for p in pts]
plt.scatter(xs,ys)
for i in range(len(txs)):
    plt.annotate(f"t={txs[i]}", (xs[i],ys[i]))
plt.show()

Lab 5 Info

Problemă

Scrieți un program Python care să determine un punct de pe o suprafață Bézier triunghiulară, folosind algoritmul lui de Casteljau. Datele de intrare vor fi:

  • gradul n al suprafeței;
  • (n+1)(n+2)/2 puncte din spațiu;
  • un triplet (u0,v0,w0) de numere reale cu suma 1,

iar rezultatul trebuie să fie r(u0,v0,w0).

Soluție

In [76]:
def iterate_idices(n):
    for i in range(n+1):
        for j in range(n+1-i):
            k = n-i-j
            yield (i,j,k)

def ix_add(i,e):
    i0,i1,i2 = i
    e0,e1,e2 = e
    return (i0+e0, i1+e1, i2+e2)

e1 = (1,0,0)
e2 = (0,1,0)
e3 = (0,0,1)

def pt_scale(u, p):
    x,y,z = p
    return (u*x,u*y,u*z)

def pt_add(*args):
    rx,ry,rz = 0,0,0
    for (x,y,z) in args:
        rx += x
        ry += y
        rz += z
    return (rx,ry,rz)
In [77]:
def deCasteljau(n, pts, u,v,w, save_intermediates = False):
    b = pts.copy()

    points = {}

    for r in range(1, n+1):
        new_b = {}
        for ix in iterate_idices(n-r):
            new_b[ix]=pt_add(
                pt_scale(u, b[ix_add(ix, e1)]),
                pt_scale(v, b[ix_add(ix, e2)]),
                pt_scale(w, b[ix_add(ix, e3)]),
            )
        if(save_intermediates): points = {**points, **b}
        b = new_b
    if(save_intermediates): points = {**points, **b}
    return b[(0,0,0)], points
In [83]:
n = 3

# punctele de control
in_pts = [
    (0, 3, 16), (1, 2, 9), (2, 1, 9), (3, 0, 16),
    (0, 2,  4), (1, 1, 5), (2, 0, 4),
    (0, 1,  1), (1, 0, 2),
    (0, 0, 5)
]

# (u, v, w)
coords = [
    (0.4, 0.2, 0.4),
    (0.5, 0.5, 0.0),
    (0.0, 0.8, 0.2),
    (0.7, 0.0, 0.3)
]
In [86]:
pts = {}
k = 0
for ix in iterate_idices(n):
    pts[ix] = in_pts[k]
    k += 1
In [91]:
print(f"n = {n}")
for k in pts.keys(): 
    print(f"b_{k[0]}{k[1]}{k[2]} = {pts[k]}")
for u0,v0,w0 in coords:
    print(f"u0,v0,w0 = {u0}, {v0}, {w0}")
    result, _ = deCasteljau(n, pts, u0, v0, w0)
    print(f"r{u0,v0,w0} = {tuple(map(lambda x:round(x,3), result))}")
n = 3
b_003 = (0, 3, 16)
b_012 = (1, 2, 9)
b_021 = (2, 1, 9)
b_030 = (3, 0, 16)
b_102 = (0, 2, 4)
b_111 = (1, 1, 5)
b_120 = (2, 0, 4)
b_201 = (0, 1, 1)
b_210 = (1, 0, 2)
b_300 = (0, 0, 5)
u0,v0,w0 = 0.4, 0.2, 0.4
r(0.4, 0.2, 0.4) = (0.6, 1.2, 5.072)
u0,v0,w0 = 0.5, 0.5, 0.0
r(0.5, 0.5, 0.0) = (1.5, 0.0, 4.875)
u0,v0,w0 = 0.0, 0.8, 0.2
r(0.0, 0.8, 0.2) = (2.4, 0.6, 12.64)
u0,v0,w0 = 0.7, 0.0, 0.3
r(0.7, 0.0, 0.3) = (0.0, 0.9, 3.344)
In [111]:
wireframe = {}  # se calculeaza niste puncte pe suprafata pentru plot
acc = 20
for i in range(acc + 1):
    for j in range(acc - i + 1):
        p, _ = deCasteljau(n, pts, i / acc, j / acc, (acc - i - j) / acc)
        wireframe[(i, j)] = p

fig = plt.figure()
ax = fig.add_subplot(projection='3d')

xs = [_[0] for _ in wireframe.values()]
ys = [_[1] for _ in wireframe.values()]
zs = [_[2] for _ in wireframe.values()]

surf = ax.plot_trisurf(xs, ys, zs, linewidth=0, zorder=10)
surf.set_facecolor((0,0,1,0.3))
fig.colorbar(surf)

for ix in pts:
    x,y,z = pts[ix]
    ax.scatter(x,y,z,color='b')
    ax.text(x,y,z,f"b{ix[0]}{ix[1]}{ix[2]}",size=10, zorder=1,color='k')

for u0, v0, w0 in coords:
    result, _ = deCasteljau(n, pts, u0, v0, w0)
    x, y, z = result
    ax.scatter(x, y, z, color='r')
    ax.text(x, y, z, f"r({u0},{v0},{w0})", size=10, zorder=1, color='k')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.view_init(-15,45)
plt.show()