Penrose-like sferne kupole: matematička konstrukcija i wireframe
Penrose-like kupole kombinuju kvazi-periodične tilinge sa zakrivljenim površinama, omogućavajući izuzetno estetske i interesantne 3D strukture. U ovoj sekciji opisujemo kako se Penrose-like raspored može projektovati na sferu ili hemisferu i kako se generiše wireframe mreža.
1. Matematika sfere
Kao i kod obične sfere, svaka tačka površine sa poluprečnikom \(R\) određena je sfernim koordinatama \((\theta, \phi)\):
\[ x = R \sin \theta \cos \phi, \quad y = R \sin \theta \sin \phi, \quad z = R \cos \theta \]
Za hemisferu koristimo \(\theta \in [0, \pi/2]\).
2. Kvazi-periodični Penrose tiling u 2D
Penrose tiling se sastoji od \( \text{thick} \) i \( \text{thin} \) rombova koji popunjavaju ravnu površinu kvazi-periodičnim rasporedom. Svaki romb se može iterativno subdividovati:
Subdivizija thick romba:
\[ \text{thick } (A,B,C,D) \rightarrow \{(A,B,P,D)_{\text{thick}}, (P,B,C,D)_{\text{thin}}\}, \quad P = A + \frac{C-A}{\phi} \]Subdivizija thin romba:
\[ \text{thin } (A,B,C,D) \rightarrow \{(A,B,C,Q)_{\text{thin}}, (A,Q,C,D)_{\text{thick}}\}, \quad Q = B + \frac{D-B}{\phi} \]Iteracijom ove procedure dobijamo kvazi-periodičan disk 2D tačaka.
3. Mapiranje 2D tilinga na sferu
Da bismo Penrose-like disk projektovali na površinu sfere, koristimo uniformno mapiranje radijalnog rastojanja na polarni ugao:
\[ \theta = \frac{r}{r_{\max}} \cdot \frac{\pi}{2}, \quad r = \sqrt{x^2 + y^2} \]Azimut \(\phi\) ostaje isti kao u 2D disku:
\[ \phi = \arctan2(y, x) \]3D koordinate dobijamo:
\[ x’ = R \sin\theta \cos\phi, \quad y’ = R \sin\theta \sin\phi, \quad z’ = R \cos\theta \]Na ovaj način, Penrose-like tačke se ravnomerno raspoređuju na hemisfernoj površini.
4. Wireframe veza rombova
Da bi mreža bila prikazana šipkama, svaka tačka romba povezuje se sa susednim tačkama u rombu (horizontalno i vertikalno), formirajući trokutasti wireframe:
- Svaka ivica romba postaje linija u 3D prostoru
- Rezultat je kvazi-periodični 3D wireframe na hemisferi
5. Primene i vizualizacija
- Arhitektura: kvazi-periodične kupole
- Wireframe prikaz za vizualizaciju i 3D štampu
- Estetski Penrose-like rasporedi na zakrivljenim površinama
Ova metoda kombinuje **matematičku preciznost** Penrose tilinga sa glatkom **sfernom geometrijom**, omogućavajući vizuelno interesantne strukture i praktičnu primenu u 3D modeliranju.

Programski kod za penrose_dome_wireframe.py:
#penrose_dome_wireframe.py
## Instalacija potrebnih zavisnosti: pip install numpy trimesh scipy
#!/usr/bin/env python3
"""
penrose_dome_wireframe.py
Generiše hemisferičnu kupolu (gornju polovinu sfere) sa Penrose-like šipkama.
"""
import numpy as np
import trimesh
from math import sin, cos, pi
from scipy.spatial import cKDTree
# -------------------- 1. Tačke na hemisferi --------------------
def fibonacci_hemisphere(samples=400, radius=1.0):
"""Ravnomerno raspoređene tačke po gornjoj hemisferi (y > 0)."""
points = []
offset = 1.0 / samples
increment = pi * (3.0 - np.sqrt(5.0)) # zlatni ugao
for i in range(samples):
y = (i * offset) # samo gornja polovina (0 do 1)
y = 1 - 2 * y
if y < 0: # preskoči donju hemisferu
continue
r = np.sqrt(1 - y * y)
phi = i * increment
x = cos(phi) * r
z = sin(phi) * r
points.append((x * radius, y * radius, z * radius))
return np.array(points)
# -------------------- 2. Povezivanje tačaka --------------------
def connect_nearest(points, k=4):
"""Povezuje svaku tačku sa k najbližih suseda (Penrose-like mreža)."""
tree = cKDTree(points)
edges = set()
for i, p in enumerate(points):
_, idxs = tree.query(p, k=k+1)
for j in idxs[1:]:
edge = tuple(sorted((i, j)))
edges.add(edge)
return list(edges)
# -------------------- 3. Kreiranje šipki --------------------
def make_cylinder(p1, p2, radius=0.01, sections=12):
"""Formira cilindar između dve tačke p1 i p2."""
from trimesh.creation import cylinder
p1 = np.array(p1)
p2 = np.array(p2)
vec = p2 - p1
length = np.linalg.norm(vec)
if length < 1e-8:
return None
cyl = cylinder(radius=radius, height=length, sections=sections)
# Orijentacija duž vektora
z_axis = np.array([0, 0, 1])
axis = np.cross(z_axis, vec)
angle = np.arccos(np.dot(z_axis, vec) / length)
if np.linalg.norm(axis) > 1e-8:
cyl.apply_transform(trimesh.transformations.rotation_matrix(angle, axis))
# Pozicioniranje u sredinu
mid = (p1 + p2) / 2
cyl.apply_translation(mid)
return cyl
# -------------------- 4. Generisanje kupole --------------------
def generate_penrose_dome(samples=400, k=3, dome_radius=1.0, thickness=0.02, color=(0.8, 0.4, 0.1)):
"""Generiše Penrose-like kupolu sa cilindarskim šipkama."""
points = fibonacci_hemisphere(samples, radius=dome_radius)
edges = connect_nearest(points, k=k)
cylinders = []
for (i, j) in edges:
cyl = make_cylinder(points[i], points[j], radius=thickness)
if cyl is not None:
cylinders.append(cyl)
mesh = trimesh.util.concatenate(cylinders)
mesh.visual.vertex_colors = np.tile(np.array(color) * 255, (len(mesh.vertices), 1))
return mesh
# -------------------- 5. Glavni deo programa --------------------
if __name__ == "__main__":
dome_mesh = generate_penrose_dome(
samples=600, # broj tačaka po kupoli
k=3, # broj povezanih suseda
dome_radius=4.0, # poluprečnik kupole
thickness=0.03, # debljina šipki
color=(0.9, 0.7, 0.1) # topla zlatna boja
)
dome_mesh.export("penrose_dome_wireframe.obj")
dome_mesh.export("penrose_dome_wireframe.ply")
print("Generisano: penrose_dome_wireframe.obj i .ply (kupola sa šipkama).")

Programski kod za penrose_sphere_wireframe.py:
#penrose_sphere_wireframe.py
# Instalacija potrebnih zavisnosti:pip install numpy trimesh
#!/usr/bin/env python3
"""
penrose_sphere_wireframe.py
Stvara sferu sa Penrose-like mrežom šipki (cilindara) po površini.
"""
import numpy as np
import trimesh
from math import sin, cos, pi
def fibonacci_sphere(samples=500, radius=1.0):
"""Distribuiše tačke kvazi-jednako po sferi koristeći Fibonacci raspored."""
points = []
offset = 2.0 / samples
increment = pi * (3.0 - np.sqrt(5.0)) # zlatni ugao
for i in range(samples):
y = ((i * offset) - 1) + (offset / 2)
r = np.sqrt(1 - y * y)
phi = i * increment
x = cos(phi) * r
z = sin(phi) * r
points.append((x * radius, y * radius, z * radius)) # skalirano po radiusu
return np.array(points)
from scipy.spatial import cKDTree
def connect_nearest(points, k=4):
"""Povezuje svaku tačku sa k najbližih suseda."""
tree = cKDTree(points)
edges = set()
for i, p in enumerate(points):
_, idxs = tree.query(p, k=k+1)
for j in idxs[1:]:
edge = tuple(sorted((i, j)))
edges.add(edge)
return list(edges)
def make_cylinder(p1, p2, radius=0.01, sections=12):
"""Formira cilindar između dve tačke p1 i p2."""
from trimesh.creation import cylinder
p1 = np.array(p1)
p2 = np.array(p2)
vec = p2 - p1
length = np.linalg.norm(vec)
if length < 1e-8:
return None
cyl = cylinder(radius=radius, height=length, sections=sections)
# orijentacija
z_axis = np.array([0, 0, 1])
axis = np.cross(z_axis, vec)
angle = np.arccos(np.dot(z_axis, vec) / length)
if np.linalg.norm(axis) > 1e-8:
cyl.apply_transform(trimesh.transformations.rotation_matrix(angle, axis))
# pozicija
mid = (p1 + p2) / 2
cyl.apply_translation(mid)
return cyl
def generate_penrose_sphere(samples=400, k=3, sphere_radius=1.0, thickness=0.015, color=(0.8, 0.5, 0.2)):
"""Generiše sferu sa šipkama kao cilindrima."""
points = fibonacci_sphere(samples, radius=sphere_radius)
edges = connect_nearest(points, k=k)
cylinders = []
for (i, j) in edges:
cyl = make_cylinder(points[i], points[j], radius=thickness)
if cyl is not None:
cylinders.append(cyl)
mesh = trimesh.util.concatenate(cylinders)
mesh.visual.vertex_colors = np.tile(np.array(color) * 255, (len(mesh.vertices), 1))
return mesh
if __name__ == "__main__":
sphere_mesh = generate_penrose_sphere(
samples=600, # broj tačaka
k=3, # broj suseda
sphere_radius=4, # <<< OVDE SE DEFINIŠE POLUPREČNIK SFERE
thickness=0.03, # debljina šipki
color=(0.9, 0.6, 0.1)
)
sphere_mesh.export("penrose_sphere_wireframe.obj")
sphere_mesh.export("penrose_sphere_wireframe.ply")
print("Generisano: penrose_sphere_wireframe.obj i .ply sa cilindrima (šipkama).")
