Program je koristan za inženjere ili entuzijaste koji žele da dizajniraju i izgrade geodetske kupole, jer automatski obračunava potrebne materijale i generiše izveštaje koji se mogu koristiti za nabavku potrebnih materijala.
Ovaj Python kod se koristi za generisanje geodetske kupole (bazirane na ikosaedru) sa mogućnošću izračunavanja i eksportovanja raznih informacija o kupoli, uključujući:
- Ulazne vrednosti:
- Frekvencija (npr. 2, 3, 4… za različite subdivizije ikosaedra).
- Prečnik kupole.
- Ugao rotacije oko Y ose.
- Ugao sečenja od vrha kupole.
- Dimenzije letvica (visina i širina).
- Geometrija kupole:
- Kreiranje kupole sa promenjivom frekvencijom i prečnikom.
- Rotacija kupole prema unetom uglu rotacije.
- Sečenje dela kupole na osnovu ugla od vrha.
- Eksport fajlova:
- STL i OBJ fajlovi sa geometrijom kupole.
- CSV fajlovi sa podacima o čvorovima, ivicama, dužinama ivica i broju letvica po čvoru.
- CSV sa podacima o dužinama letvica i njihovoj boji (na osnovu dužine).
- Montaža šipki u PLY i GLB formatima.
- Statistički podaci:
- Ukupna dužina letvica.
- Kubikaža letvica (zapremina drvene konstrukcije).
- Površina kupole, zapremina kupole, pokrivena površina na tlu.
- Težina drvene konstrukcije, bazirana na gustini drveta.
Kod koristi biblioteku Trimesh za 3D geometriju i kreiranje objekata, a Matplotlib i CSV za vizualizaciju i izvoz podataka.
Instalacija svih potrebnih zavisnosti:
python3 -m pip install numpy trimesh matplotlib
Prograski kod za geo.py
#geo.py
# Instalacija potrebnih zavisnosti:python3 -m pip install numpy trimesh matplotlib
# 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.
"""
---------------------------------------------------------
Program: Dome Generator & Analyzer
Opis: Program za generisanje geodetske kupole bazirane na ikosaedru, sa mogućnostima izvoza u STL, OBJ, PLY, i GLB formate, kao i izračunavanja statistike i analiza konstrukcije.
Verzija: 1.0.0
Autor: Aleksandar Maričić
Datum: April 2025
Kopirajt: © 2025 Aleksandar Maričić. Sva prava zadržana.
Licenca: MIT License
Zahtevi: Python 3.7+, Trimesh, NumPy, Matplotlib
---------------------------------------------------------
"""
import numpy as np
import trimesh
import matplotlib.pyplot as plt
import csv
from collections import Counter
import os
# Unos frekvencije
try:
frekvencija = float(input("Unesi frekvenciju (npr. 3V je 2 ): ") or 2)
except ValueError:
print("Nevazeci unos! Koristi se podrazumevani 2")
frekvencija = 2
# Unos prečnika
try:
diameter = float(input("Unesi prečnik kupole u cm (npr. 600): ") or 600)
except ValueError:
print("Nevazeci unos! Koristi se podrazumevani prečnik 600 cm")
diameter = 600
# Unos ugla rotacije
try:
angle_deg = float(input("Rotacija oko Y ose (npr. 58.2825): ") or 58.2825)
except ValueError:
print("Nevazeci unos! Koristi se podrazumevani 58.2825")
angle_deg = 58.2825
# Unos ugla sečenja
try:
stepena_od_vrha = float(input("Sečenje od vrha (npr. 85): ") or 85)
except ValueError:
print("Nevazeci unos! Koristi se podrazumevani 85")
stepena_od_vrha = 85
# Visina i širina letvica
try:
stick_height = float(input("Unesi visinu letvice u cm (npr. 5 ): ") or 5)
except ValueError:
stick_height = 5
try:
stick_width = float(input("Unesi širinu letvice u cm (npr. 3 ): ") or 3)
except ValueError:
stick_width = 3
file_base = f"{int(frekvencija)}V_{diameter:.2f}_{angle_deg:.2f}_{stepena_od_vrha:.2f}"
sphere = trimesh.creation.icosphere(subdivisions=int(frekvencija))
angle_rad = np.deg2rad(angle_deg)
rotation_matrix = trimesh.transformations.rotation_matrix(angle_rad, [0, 1, 0])
sphere.apply_transform(rotation_matrix)
scale_factor = diameter / sphere.bounding_box.extents.max()
sphere.apply_scale(scale_factor)
full_radius = diameter / 2
height_cutoff = -full_radius * np.cos(np.deg2rad(stepena_od_vrha))
vertex_mask = sphere.vertices[:, 2] >= height_cutoff
vertex_indices = np.nonzero(vertex_mask)[0]
old_to_new = {old: new for new, old in enumerate(vertex_indices)}
new_faces = []
for face in sphere.faces:
if all(v in vertex_indices for v in face):
new_faces.append([old_to_new[v] for v in face])
new_vertices = sphere.vertices[vertex_indices]
dome = trimesh.Trimesh(vertices=new_vertices, faces=np.array(new_faces), process=True)
dome.export(f"{file_base}.stl")
dome.export(f"{file_base}.obj")
with open(f"{file_base}_cvorovi.csv", 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Index', 'X', 'Y', 'Z'])
for i, v in enumerate(dome.vertices):
writer.writerow([i] + list(v))
edges = dome.edges_unique
edge_lengths = dome.edges_unique_length
with open(f"{file_base}_ivice.csv", 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Index', 'Cvor1', 'Cvor2', 'Duzina'])
for i, (e, l) in enumerate(zip(edges, edge_lengths)):
writer.writerow([i, e[0], e[1], f"{l:.5f}"])
# Grupisanje po zaokruženim dužinama
rounded_lengths = [round(l, 5) for l in edge_lengths]
length_counter = Counter(rounded_lengths)
# Paleta boja
color_palette = [
[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0],
[1.0, 1.0, 0.0], [1.0, 0.0, 1.0], [0.0, 1.0, 1.0],
[0.5, 0.5, 0.5], [0.8, 0.3, 0.3], [0.3, 0.8, 0.3],
[0.3, 0.3, 0.8]
]
color_names = {
(1.0, 0.0, 0.0): 'Crvena',
(0.0, 1.0, 0.0): 'Zelena',
(0.0, 0.0, 1.0): 'Plava',
(1.0, 1.0, 0.0): 'Žuta',
(1.0, 0.0, 1.0): 'Magenta',
(0.0, 1.0, 1.0): 'Cijan',
(0.5, 0.5, 0.5): 'Siva',
(0.8, 0.3, 0.3): 'Svetlo crvena',
(0.3, 0.8, 0.3): 'Svetlo zelena',
(0.3, 0.3, 0.8): 'Svetlo plava'
}
# Mapiranje dužina na boje
length_color_map = {}
for idx, (length, _) in enumerate(sorted(length_counter.items())):
color = color_palette[idx % len(color_palette)]
length_color_map[length] = color
# Snimanje letvi u CSV sa bojom
with open(f"{file_base}_letve.csv", 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Duzina', 'Broj ivica', 'Boja'])
for length, count in sorted(length_counter.items()):
color_rgb = tuple(length_color_map.get(length, (0.2, 0.2, 0.2)))
color_name = color_names.get(color_rgb, f"RGB {color_rgb}")
writer.writerow([f"{length:.5f}", count, color_name])
# Ispis u konzolu
print("\nGrupisane dužine ivica:")
for length, count in sorted(length_counter.items()):
color_rgb = tuple(length_color_map.get(length, (0.2, 0.2, 0.2)))
color_name = color_names.get(color_rgb, f"RGB {color_rgb}")
print(f"{color_name}: {length:.5f} -> Broj ivica: {count}")
# Dodavanje obojenih šipki
boxes = []
for edge, raw_length in zip(edges, edge_lengths):
p1 = dome.vertices[edge[0]]
p2 = dome.vertices[edge[1]]
center = (p1 + p2) / 2
direction = p2 - p1
length = np.linalg.norm(direction)
if length == 0:
continue
direction /= length
up = np.array([0, 0, 1])
if np.allclose(direction, up):
up = np.array([0, 1, 0])
side1 = np.cross(up, direction)
side1 /= np.linalg.norm(side1)
side2 = np.cross(direction, side1)
T = np.eye(4)
T[:3, :3] = np.column_stack([side1 * stick_width, side2 * stick_height, direction * length])
T[:3, 3] = center
box = trimesh.creation.box(extents=(1, 1, 1))
box.apply_transform(T)
rounded = round(length, 5)
color = length_color_map.get(rounded, [0.2, 0.2, 0.2])
box.visual.vertex_colors = np.tile(color, (box.vertices.shape[0], 1))
boxes.append(box)
# Eksport obojenih šipki
if boxes:
all_sticks = trimesh.util.concatenate(boxes)
all_sticks.export(f"{file_base}_montaza.ply")
all_sticks.export(f"{file_base}_web.glb")
print(f"\n Šipke su eksportovane u {file_base}_montaza.ply")
print(f"\n Šipke su eksportovane u {file_base}_web.glb")
print("\n Geodetska kupola je uspešno kreirana i sačuvana!")
# Brojanje susednih ivica za svaki čvor
from collections import defaultdict
node_connections = defaultdict(int)
for e in edges:
node_connections[e[0]] += 1
node_connections[e[1]] += 1
broj_3 = sum(1 for count in node_connections.values() if count == 3)
broj_4 = sum(1 for count in node_connections.values() if count == 4)
broj_5 = sum(1 for count in node_connections.values() if count == 5)
broj_6 = sum(1 for count in node_connections.values() if count == 6)
print(f"\n Čvorišta sa 3 letvice: {broj_3}")
print(f" Čvorišta sa 4 letvice: {broj_4}")
print(f" Čvorišta sa 5 letvica: {broj_5}")
print(f" Čvorišta sa 6 letvica: {broj_6}")
with open(f"{file_base}_cvorista.csv", 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Index', 'X', 'Y', 'Z', 'BrojLetvica'])
for i, v in enumerate(dome.vertices):
count = node_connections.get(i, 0)
writer.writerow([i] + list(v) + [count])
# -------------------
# Statistika kupole
# -------------------
# Ukupna dužina svih letvica
ukupna_duzina_cm = sum(edge_lengths)
ukupna_duzina_m = ukupna_duzina_cm / 100
# Kubikaža letvica (pretpostavka: pravougaoni presek, dužina * visina * širina)
# Sve u metrima
volumen_m3 = ukupna_duzina_m * (stick_width / 100) * (stick_height / 100)
# Površina kupole (trouglasta mreža)
povrsina_cm2 = dome.area
povrsina_m2 = povrsina_cm2 / 10000 # iz cm² u m²
# Zapremina kupole (sferni isečak - spherical cap)
R = diameter / 2 / 100 # poluprečnik u metrima
h = (max(dome.vertices[:, 2]) - min(dome.vertices[:, 2])) / 100 # visina kupole u metrima
zapremina_m3 = (1/3) * np.pi * h**2 * (3*R - h)
# Površina koju kupola pokriva na tlu (projekcija baze)
# Nađi najveći radijalni domet (u XY ravni)
xy_coords = dome.vertices[:, :2]
udaljenosti_od_centra = np.linalg.norm(xy_coords, axis=1)
efektivni_poluprecnik = np.max(udaljenosti_od_centra) / 100 # u metrima
pokrivena_povrsina_m2 = np.pi * efektivni_poluprecnik**2
# Gustina drveta (u kg/m³)
gustina_kg_m3 = 500 # može se promeniti npr. za hrast ~750 kg/m³
# Ukupna masa konstrukcije
masa_kg = volumen_m3 * gustina_kg_m3
# Ispis
print("\n Statistika konstrukcije:")
print(f" Ukupna dužina letvica: {ukupna_duzina_m:.2f} m")
print(f" Kubikaža letvica (za {stick_width}x{stick_height} cm): {volumen_m3:.3f} m³")
print(f" Površina kupole: {povrsina_m2:.2f} m²")
print(f" Zapremina kupole: {zapremina_m3:.2f} m³")
print(f" Površina koju kupola pokriva na tlu: {pokrivena_povrsina_m2:.2f} m²")
print(f" Težina drvene konstrukcije: {masa_kg:.2f} kg")
with open(f"{file_base}_statistika.txt", 'w') as f:
f.write(" Statistika konstrukcije\n")
f.write(f" Ukupna dužina letvica: {ukupna_duzina_m:.2f} m\n")
f.write(f" Kubikaža letvica (za {stick_width}x{stick_height} cm): {volumen_m3:.3f} m³\n")
f.write(f" Površina kupole: {povrsina_m2:.2f} m²\n")
f.write(f" Zapremina kupole: {zapremina_m3:.2f} m³\n")
f.write(f" Površina koju kupola pokriva na tlu: {pokrivena_povrsina_m2:.2f} m²\n")
f.write(f" Težina drvene konstrukcije: {masa_kg:.2f} kg\n")
Primer rada u programa geo.py

