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).")