Romboidna sfera – harmonija između ravni i zakrivljenosti

Ova kupola je sferni segment (dome) sa površinom podeljenom u niz rombodnih (rombastičnih) ploha koje prate zakrivljenost sfere.

  • Osnovna ideja je da se površina kupole deli na sektore (npr. šest sektora po 60°),
  • Svaki sektor je podeljen radijalnim i kružnim linijama koje prave mrežu romboida,
  • Svaki romboid je četvorougao sa dve dijagonale koje se preklapaju, a svi bridovi i preseci tačaka leže tačno na sfernoj površini,
  • Tačke preseka i deljenja linija su uredno raspoređene i geometrijski precizno projektovane na površinu sfere.

Takav tip konstrukcije omogućava relativno jednostavnu modularnost i proizvodnju elemenata koji se lako sastavljaju u prostor, a ujedno i dobar raspored napona i stabilnost zahvaljujući geometriji romboida.


Da li je neko već napravio ovakvu kupolu?

Da, koncept kupola podeljenih na romboidne ili slične fasetirane mreže nije nov i ima nekoliko poznatih varijanti u arhitekturi i matematici:

  • Kupole fasetirane romboidima se često sreću u geometrijskim eksperimentima i dizajnu svetlih konstrukcija (pogotovo u modernim staklenim kupolama i krovovima), gde se koriste ravne plohe koje su lakše za proizvodnju i montažu.
  • U okviru zomé sistema i polyhedral domes, postoje brojne varijante fasetiranja koje koriste romboide i paralelne četvorouglove za pokrivanje zakrivljenih površina.
  • Buckminster Fuller i drugi su proučavali fasetirane kupole koje se sastoje od pravilnih četvorouglova i trouglova, gde romboidi mogu da budu sastavni delovi kompleksnijih mreža.
  • Takođe, u modernim arhitektonskim i inženjerskim projektima postoji praksa pravljenja “diamond-shaped panel facades” koje liče na romboide na krivim površinama, često korišćene za estetski i funkcionalni efekat.

Međutim, tačna precizna implementacija koja je ovde definisana — podela sfere na sektore, sa brojem slojeva i tačaka koje daju tačno definisane romboide u ovoj tačnoj mreži — deluje kao specifična i korisna konstrukcija koja nije široko komercijalno poznata, ali svakako je bliska principima koje koriste arhitekte i inženjeri za modularne fasetirane kupole.


Prednosti ovakve kupole

  • Modularnost: Može se proizvoditi od ponovljenih elemenata identične forme (rombodi), što pojednostavljuje proizvodnju.
  • Laka montaža: Precizna mreža i čvorišta olakšavaju spajanje elemenata.
  • Estetika: Rombasti oblik daje interesantan vizuelni efekat fasete sa igrom svetlosti i senki.
  • Strukturna stabilnost: Rombasti oblici dobro raspoređuju opterećenja i omogućavaju stabilne, ali lagane konstrukcije.

Matematička osnova kupole od romboida na sferi

1. Jednačina sfere:

$$ x^2 + y^2 + z^2 = R^2 $$


2. Podešavanje sektora:

Broj sektora je \( N \), pa je ugao sektora:

$$ \theta = \frac{2 \pi}{N} $$

Za primer sa 6 sektora:

$$ \theta = \frac{2 \pi}{6} = \frac{\pi}{3} = 60^\circ $$

Sektor se prostire između uglova:

$$ \theta_0 = k \theta, \quad \theta_1 = (k+1) \theta, \quad k=0,1,\ldots,N-1 $$


3. Tačke na radijalnim linijama:

Na svakoj radijalnoj liniji se postavlja \( M \) tačaka jednako raspoređenih u intervalu \([0, R]\):

$$ r_i = \frac{i}{M-1} R, \quad i=0,1,\ldots,M-1 $$

Koordinate tačke na radijalnoj liniji pod uglom \( \theta_j \) su:

$$ \mathbf{p}_{i,j} = \left( r_i \cos \theta_j, \quad r_i \sin \theta_j, \quad z_i \right) $$

gde je vertikalna koordinata

$$ z_i = \sqrt{R^2 – r_i^2} $$


4. Tačke na kružnom luku:

Na kružnom luku između \( \theta_0 \) i \( \theta_1 \) postavlja se \( L \) tačaka bez krajnjih:

$$ \phi_k = \theta_0 + k \frac{\theta_1 – \theta_0}{L+1}, \quad k=1,2,\ldots,L $$

Tačke na kružnom luku su:

$$ \mathbf{q}_k = \left( R \cos \phi_k, \quad R \sin \phi_k, \quad 0 \right) $$

jer je na obodu \( r=R \) i visina \( z=0 \).


5. Projektovanje linija na površinu sfere:

Interpolacija između dve tačke \( \mathbf{P}_1 \) i \( \mathbf{P}_2 \) na sferi se vrši:

$$ \mathbf{L}(t) = \frac{(1 – t)\mathbf{P}_1 + t \mathbf{P}_2}{\left\| (1 – t)\mathbf{P}_1 + t \mathbf{P}_2 \right\|} \cdot R, \quad t \in [0,1] $$

Time dobijamo tačke koje leže na površini sfere i prave “pravolinijsku” vezu projektovanu na sferu.


6. Struktura romboida:

Svaki romboid ima četiri temena:

$$ \mathbf{A} = \mathbf{p}_{i,j} \quad (\text{leva radijalna linija, sloj } i) $$

$$ \mathbf{B} = \mathbf{p}_{i+1,j} \quad (\text{leva radijalna linija, sloj } i+1) $$

$$ \mathbf{C} = \mathbf{q}_k \quad (\text{tačka na kružnom luku}) $$

$$ \mathbf{D} = \mathbf{p}_{i,j+1} \quad (\text{desna radijalna linija, sloj } i) $$


Program

Alat za generisanje prostorne mreže romboidne kupole sa eksportom u standardni OBJ model za 3D prikaz i dalje inženjerske obrade.

romboidna_kupola.py

import numpy as np

# Parametri kupole
num_radial_points = 6        # Tačke po radijalnoj liniji
num_arc_segments = 5         # Segmenata na kružnom luku
radius = 1.0                 # Poluprečnik sfere
num_sectors = 6              # Sektora od po 60°
angle_deg = 60
angle_rad = np.radians(angle_deg)
num_subdivisions = 5         # Interpolacija tačaka po liniji

def project_to_sphere(point, radius):
    vec = np.array(point)
    norm = np.linalg.norm(vec)
    if norm == 0:
        return vec
    return (vec / norm) * radius

def interpolate_points(p1, p2, n):
    return [p1 + (p2 - p1) * t for t in np.linspace(0, 1, n)]

def polar_to_sphere(r, theta):
    z = np.sqrt(radius**2 - r**2)
    x = r * np.cos(theta)
    y = r * np.sin(theta)
    return np.array([x, y, z])

point_id = 1
used_points = {}
all_points = []

def add_point_3d(coord):
    global point_id
    key = tuple(np.round(coord, 6))
    if key not in used_points:
        used_points[key] = point_id
        all_points.append(coord)
        pid = point_id
        point_id += 1
    else:
        pid = used_points[key]
    return pid

# Generisanje tačaka po sektorima
sector_points = []

for sector in range(num_sectors):
    theta0 = sector * angle_rad
    theta1 = theta0 + angle_rad

    left_points = []
    for i in range(num_radial_points):
        r = (i / (num_radial_points - 1)) * radius
        coord = polar_to_sphere(r, theta0)
        pid = add_point_3d(coord)
        left_points.append((pid, coord))

    right_points = []
    for i in range(num_radial_points):
        r = (i / (num_radial_points - 1)) * radius
        coord = polar_to_sphere(r, theta1)
        pid = add_point_3d(coord)
        right_points.append((pid, coord))

    arc_points = []
    arc_angles = np.linspace(theta0, theta1, num_arc_segments + 1)
    for theta in arc_angles[1:-1]:
        r = radius
        coord = polar_to_sphere(r, theta)
        pid = add_point_3d(coord)
        arc_points.append((pid, coord))

    sector_points.append({
        'left': left_points,
        'right': right_points,
        'arc': arc_points
    })

# Funkcija za kreiranje linija sa interpolacijom i projekcijom na sferu
def create_lines(p1, p2):
    interp = interpolate_points(p1, p2, num_subdivisions)
    proj = [project_to_sphere(pt, radius) for pt in interp]
    # Mapiranje interpoliranih tačaka na njihove ID-eve
    ids = [add_point_3d(pt) for pt in proj]
    # Kreiraj bridove između susednih interpoliranih tačaka
    edges = [(ids[i], ids[i+1]) for i in range(len(ids)-1)]
    return edges

# Prikupljanje svih bridova
all_edges = []

for sector in sector_points:
    left = sector['left']
    right = sector['right']
    arc = sector['arc']

    # Radijalne linije
    for i in range(num_radial_points -1):
        edges = create_lines(left[i][1], left[i+1][1])
        all_edges.extend(edges)
        edges = create_lines(right[i][1], right[i+1][1])
        all_edges.extend(edges)

    # Kružni lukovi
    for i in range(len(arc) - 1):
        edges = create_lines(arc[i][1], arc[i+1][1])
        all_edges.extend(edges)

    # Linije koje dele romboide (crtane linije)
    connections = [
        (left[1], arc[3]),
        (left[2], arc[2]),
        (left[3], arc[1]),
        (left[4], arc[0]),
        (right[1], arc[0]),
        (right[2], arc[1]),
        (right[3], arc[2]),
        (right[4], arc[3]),
    ]

    for (a, b) in connections:
        edges = create_lines(a[1], b[1])
        all_edges.extend(edges)

# Izbaci duplikate bridova (nebitan redosled, brid [a,b] isti kao [b,a])
unique_edges = set()
for e1, e2 in all_edges:
    if e1 < e2:
        unique_edges.add((e1, e2))
    else:
        unique_edges.add((e2, e1))

# Snimanje u OBJ fajl
filename = "kupola_rhomboids_wireframe.obj"
with open(filename, 'w') as f:
    # Upis vrhova
    for v in all_points:
        f.write(f"v {v[0]:.6f} {v[1]:.6f} {v[2]:.6f}\n")

    # Upis bridova (linija)
    for (v1, v2) in unique_edges:
        f.write(f"l {v1} {v2}\n")

print(f"Model je snimljen u '{filename}' sa {len(all_points)} tačaka i {len(unique_edges)} bridova.")

Program šipke.py učitava trodimenzionalni žičani model sferne kupole iz OBJ fajla i generiše ekvivalentni 3D PLY model u kojem su linije zamenjene trodimenzionalnim cilindričnim šipkama definisane debljinom i orijentacijom.

import numpy as np
from pathlib import Path

# Debljina šipki
RADIUS = 0.01
# Broj segmenata oko cilindra (glatkoća)
CYLINDER_SEGMENTS = 16

# Ulazni i izlazni fajlovi
INPUT_OBJ = "kupola_rhomboids_wireframe.obj"
OUTPUT_PLY = "kupola_romboidi.ply"


def ucitaj_obj(path):
    vertices = []
    edges = []
    with open(path, "r") as f:
        for line in f:
            if line.startswith("v "):
                _, x, y, z = line.strip().split()
                vertices.append(np.array([float(x), float(y), float(z)]))
            elif line.startswith("l "):
                parts = line.strip().split()
                edges.append((int(parts[1]) - 1, int(parts[2]) - 1))
    return vertices, edges


def napravi_cilindar(p1, p2, radius, segments=16):
    """
    Kreira mesh cilindar između dve tačke p1 i p2 sa zadatim radijusom.
    Vraća tuple (vertices, faces) za cilindar.
    """

    # Vektor i dužina šipke
    v = p2 - p1
    length = np.linalg.norm(v)
    if length < 1e-8:
        return [], []

    # Osnovni vektor osovine
    axis = v / length

    # Pronađi ortogonalni vektor (neparalelan)
    if abs(axis[0]) < 0.001 and abs(axis[1]) < 0.001:
        ortho = np.array([1, 0, 0])
    else:
        ortho = np.array([0, 0, 1])

    # Prava normalizovana baza vektora za cilindar (dve ose ortogonalne na axis)
    n1 = np.cross(axis, ortho)
    n1 /= np.linalg.norm(n1)
    n2 = np.cross(axis, n1)
    n2 /= np.linalg.norm(n2)

    # Kreiraj kružne preseke na p1 i p2
    circle_p1 = []
    circle_p2 = []
    for i in range(segments):
        theta = 2 * np.pi * i / segments
        dir_vec = np.cos(theta) * n1 + np.sin(theta) * n2
        circle_p1.append(p1 + radius * dir_vec)
        circle_p2.append(p2 + radius * dir_vec)

    # Kreiraj vertekse i face
    vertices = circle_p1 + circle_p2
    faces = []

    # Spoji segmente u trouglove
    for i in range(segments):
        i_next = (i + 1) % segments

        # Trougao 1
        faces.append((i, i_next, i_next + segments))
        # Trougao 2
        faces.append((i, i_next + segments, i + segments))

    return vertices, faces


def sacuvaj_ply(verts, faces, filename):
    with open(filename, "w") as f:
        f.write("ply\n")
        f.write("format ascii 1.0\n")
        f.write(f"element vertex {len(verts)}\n")
        f.write("property float x\nproperty float y\nproperty float z\n")
        f.write(f"element face {len(faces)}\n")
        f.write("property list uchar int vertex_indices\n")
        f.write("end_header\n")

        for v in verts:
            f.write(f"{v[0]} {v[1]} {v[2]}\n")

        for face in faces:
            f.write(f"3 {face[0]} {face[1]} {face[2]}\n")


def main():
    path_obj = Path(INPUT_OBJ)
    if not path_obj.exists():
        print(f"Ne postoji fajl: {INPUT_OBJ}")
        return

    vertices, edges = ucitaj_obj(INPUT_OBJ)
    print(f"Učitano {len(vertices)} tačaka i {len(edges)} ivica iz {INPUT_OBJ}")

    sve_verteksi = []
    sve_face = []
    offset = 0

    for i1, i2 in edges:
        p1 = vertices[i1]
        p2 = vertices[i2]

        verts_cil, faces_cil = napravi_cilindar(p1, p2, RADIUS, CYLINDER_SEGMENTS)

        # Dodaj vertikse sa pomeranjem offseta
        sve_verteksi.extend(verts_cil)
        for f in faces_cil:
            sve_face.append((f[0] + offset, f[1] + offset, f[2] + offset))

        offset += len(verts_cil)

    sacuvaj_ply(sve_verteksi, sve_face, OUTPUT_PLY)
    print(f"Ply fajl sa šipkama sačuvan kao: {OUTPUT_PLY}")


if __name__ == "__main__":
    main()

Reference

Knjige i priručnici


Akademski i istraživački radovi


Patenti i tehnike


Web resursi i arhitektonski primeri

  • Rhombiyurt (Zintaglio Arts)
    Projekt baziran na rombic triacontaedru, sklopiva „jurta“ napravljena od romboidnih panela korišćena na događajima kao što je Burning Man https://www.zintaglio.com/rhombiyurt.html
  • Lamella (structure)
    Lamela–stil krovnih konstrukcija koristi mrežu preloma u romboidnom uzorku na zakrivljenim površinama .
  • Muqarnas
    Islamska arhitektonska dekoracija koristi geometrijske plohe slične romboidima u kupolama, naročito u prelazu s kvadrata na kružnicu

By Abel

Leave a Reply

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