Nautilus: kako priroda misli u formi

Pokušaj da se pronađe gotov program koji verno modeluje trodimenzionalnu školjku nautilusa na internetu pokazao se kao iznenađujuće težak zadatak. U moru vizuelizacija, ilustracija i uprošćenih simulacija, retko se nailazi na rešenje koje kombinuje matematičku preciznost sa estetskom vernošću prirodnom obliku. Većina dostupnih modela su statične replike, često ograničene na spoljašnju formu spirale, bez unutrašnje logike rasta, bez promenljivog preseka i bez vernog odnosa zlatnog preseka i konične geometrije.

Tražio sam među otvorenim izvorima, naučnim simulacijama, CAD bibliotekama, pa čak i umetničkim projektima – ali nijedno rešenje nije sadržavalo dinamički rast spirale sa promenljivim eliptičnim presekom, niti poštovalo zakonitosti prirodne spirale, kako ih definišu zlatni ugao, Fibonaccijev niz i eksponencijalni rast radijusa.

Na kraju je postalo jasno: ako prirodu želimo da razumemo, moramo je sami izgraditi – od nule, kod po kod. Tako je nastao ovaj program: kao odgovor na odsustvo, kao pokušaj da se digitalnim sredstvima rekonstruiše unutrašnja logika nautilusa, a ne samo njegova površina.

Ovaj program nije tek algoritam koji crta školjku – to je filozofski model postojanja. On u matematičkom jeziku izražava princip prirode da raste bez preklapanja, da se širi bez narušavanja sklada. Spirala koju gradi ovaj kod nije samo forma – ona je metafora: za život, za razvoj, za odnos pojedinca prema celini.

U temelju algoritma leži zlatni presek (1.618…), broj koji se kroz vekove javlja kao znak ravnoteže između rasta i harmonije. Spirala školjke – utemeljena na ovoj proporciji – raste eksponencijalno, ali nikada ne dostiže tačku samodestrukcije. Svaki novi obrt obuhvata prethodni, ali ga ne poništava. Time se prikazuje načelo kontinuiteta: prošlost živi u sadašnjosti, sadašnjost rađa budućnost, bez loma.

Unutar koda vibrira i Fibonaccijev niz, ne samo kao sekvenca brojeva, već kao arhetip rasta. Rast koji se ne nameće, već izrasta iz prethodnog. Svaki parametar – radijus, visina, eliptični presek – menja se u skladu s tim principom. Rezultat nije samo 3D model, već vizuelna meditacija o tome kako priroda misli.

Rotacija preseka, širenje spirale, uspon duž konusa – sve su to matematički izrazi jednog dubljeg zakona: zakona spiralne etike, prema kojoj se sve kreće napred, ali nikada na štetu prethodnog. To je rast sa granicom, moć sa merom, lepota sa logikom.

Na kraju, ovaj program prikazuje kako život može rasti bez konflikta. Kako svaka forma, ako je u skladu s prirodnim zakonima, može postati istovremeno funkcionalna i lepa. Školjka nautilusa, izgrađena kodom, postaje digitalni odraz univerzalne istine:

Biti – a ne smetati. Rasti – a ne rušiti. Oblikovati – a ostati u skladu.


Matematika 3D modela školjke Nautilusa

Ovaj algoritam gradi školjku po principu logaritamske spirale na konusnoj površini sa eliptičnim, rotirajućim presekom.

1. Spirala rasta

Radijalna funkcija spirale definisana je kao:

$$ r(t) = r_0 \cdot e^{k t} $$

Visina duž konusa:

$$ z(t) = \frac{r(t)}{\tan(\theta_c)} $$

2. Položaj tačke u 3D prostoru

$$ \vec{C}(t) = \left( r(t) \cdot \cos t,\quad r(t) \cdot \sin t,\quad \frac{r(t)}{\tan(\theta_c)} \right) $$

3. Tangenta spirale

$$ \vec{T}(t) = \left( \frac{dr}{dt} \cos t – r \sin t,\quad \frac{dr}{dt} \sin t + r \cos t,\quad \frac{dr}{dt} / \tan(\theta_c) \right) $$

4. Lokalni koordinatni sistem

$$ \vec{N} = \frac{\vec{T} \times \vec{U}}{ \|\vec{T} \times \vec{U} \| }, \quad \vec{B} = \vec{T} \times \vec{N} $$

5. Eliptični presek

$$ a(t) = a_0 + f (a_1 – a_0), \quad b(t) = b_0 + f (b_1 – b_0) $$ $$ (x_e, y_e) = (a \cos \phi, b \sin \phi) $$

Rotacija preseka za ugao \( \alpha(t) = \omega t \):

$$ R = \begin{bmatrix} \cos \alpha & -\sin \alpha \\ \sin \alpha & \cos \alpha \end{bmatrix} $$

Projekcija u 3D prostor:

$$ \vec{P}(t, \phi) = \vec{C}(t) + x_e \cdot \vec{N} + y_e \cdot \vec{B} $$

Ova procedura se ponavlja duž spirale, generišući mrežu trouglova koja opisuje punu 3D formu nautilus školjke.


Programski kod nautilus7.py

# The MIT License (MIT)
# Copyright (c) 2025 Aleksandar Maričić
#
# Ovim se omogućava bilo kome da koristi, kopira, menja, spaja, objavljuje,
# distribuira, daje podlicencu i/ili prodaje kopije ovog softverskog programa,
# uz uslov da u svim kopijama ili značajnim delovima softverskog programa bude
# uključena sledeća obavest:
#
# Copyright (c) 2025 Aleksandar Maričić
#
# Ovaj softverski program je pružen "takav kakav jeste", bez bilo kakvih garancija,
# izričitih ili impliciranih, uključujući, ali ne ograničavajući se na, garancije o
# prikladnosti za prodaju ili pogodnosti za određenu svrhu. U svakom slučaju, autori
# ili nosioci prava nisu odgovorni za bilo kakvu štetu ili druge obaveze koje mogu nastati
# usled upotrebe ovog softverskog programa.

import numpy as np
import trimesh
         #1,1,2,3,5,8,13,21,34,55,
def nautilus_conical_shell_variable_aperture(
    turns=1.6180339887,  # Zlatni presek
    points_per_turn=300,
    r0=1.6180339887,     # Zlatni presek
    expansion_rate=0.2,
    translation_rate=0.1,
    aperture_a0=0.05,
    aperture_b0=0.03,
    aperture_a1=13,
    aperture_b1=13,
    aperture_rotation_speed=3, #3.0,
    cone_angle=np.pi / 6,
    resolution=64
):
    """
    Generiše nautilus školjku sa promenljivim presekom (od vrha do dna),
    na konusnoj površini.

    Parametri:
    - turns: broj okretaja spirale
    - points_per_turn: tačaka po okretaju
    - r0: početni radijus spirale (spoljni položaj)
    - expansion_rate: stopa širenja radijusa spirale
    - translation_rate: vertikalni rast duž ose
    - aperture_a0, aperture_b0: poluosi preseka na vrhu (najmanji presek)
    - aperture_a1, aperture_b1: poluosi preseka na dnu (najveći presek)
    - aperture_rotation_speed: brzina rotacije preseka
    - cone_angle: ugao konusa
    - resolution: broj tačaka preseka
    """

    N = int(turns * points_per_turn)
    theta = np.linspace(0, 2 * np.pi * turns, N)

    vertices = []
    faces = []

    for i, t in enumerate(theta):
        # Računanje radijusa i visine
        r = r0 * np.exp(expansion_rate * t)
        z = r / np.tan(cone_angle)

        center = np.array([r * np.cos(t), r * np.sin(t), z])

        # Tangenta na spirali na konusu
        dr_dt = expansion_rate * r
        dx_dt = dr_dt * np.cos(t) - r * np.sin(t)
        dy_dt = dr_dt * np.sin(t) + r * np.cos(t)
        dz_dt = dr_dt / np.tan(cone_angle)

        tangent = np.array([dx_dt, dy_dt, dz_dt])
        tangent /= np.linalg.norm(tangent)

        # Normalni i binormalni vektori za presek
        up = np.array([0, 0, 1])
        normal = np.cross(tangent, up)
        if np.linalg.norm(normal) < 1e-8:
            normal = np.array([1, 0, 0])
        normal /= np.linalg.norm(normal)

        binormal = np.cross(tangent, normal)
        binormal /= np.linalg.norm(binormal)

        # Interpolacija poluosa preseka između vrha i dna
        frac = i / (N - 1)
        a = aperture_a0 + frac * (aperture_a1 - aperture_a0)
        b = aperture_b0 + frac * (aperture_b1 - aperture_b0)

        # Rotacija preseka
        angle_rot = aperture_rotation_speed * t
        t_param = np.linspace(0, 2 * np.pi, resolution, endpoint=False)

        ellipse_local = np.stack((a * np.cos(t_param), b * np.sin(t_param)), axis=1)

        cos_r = np.cos(angle_rot)
        sin_r = np.sin(angle_rot)
        rot_matrix_2d = np.array([[cos_r, -sin_r],
                                  [sin_r,  cos_r]])
        ellipse_rotated = ellipse_local @ rot_matrix_2d.T

        # Presek u 3D prostoru
        ellipse_3d = np.outer(ellipse_rotated[:, 0], normal) + np.outer(ellipse_rotated[:, 1], binormal)
        ellipse_3d += center

        vertices.extend(ellipse_3d.tolist())

        # Kreiranje trouglova za mesh
        if i > 0:
            prev_offset = (i - 1) * resolution
            curr_offset = i * resolution
            for j in range(resolution):
                v0 = prev_offset + j
                v1 = prev_offset + (j + 1) % resolution
                v2 = curr_offset + j
                v3 = curr_offset + (j + 1) % resolution

                faces.append([v0, v1, v2])
                faces.append([v1, v3, v2])

    vertices = np.array(vertices)
    faces = np.array(faces)

    mesh = trimesh.Trimesh(vertices=vertices, faces=faces, process=False)
    return mesh


if __name__ == "__main__":
    mesh = nautilus_conical_shell_variable_aperture()

    mesh.export("nautilus_variable_aperture.obj")
    print("Fajl 'nautilus_variable_aperture.obj' je sačuvan.")

By Abel

Leave a Reply

Your email address will not be published. Required fields are marked *