Ovaj program je alat za analizu geodetskih kupola u formatu STL, koji omogućava korisnicima da generišu 3D model letvica (šipki) sa bojama na osnovu dužine ivica i izvoze ga u PLY format. Program takođe računa različite statistike vezane za strukturu kupole, kao što su ukupna dužina letvica, volumen, površina, masa i zapremina kupole, i generiše izveštaj u .txt fajlu.
Evo šta program radi:
- Unos fajla i parametara:
- Program traži od korisnika da unese
.stlfajl sa modelom kupole. Ako fajl nije pronađen, program se zaustavlja. - Takođe, korisnik unosi parametre za visinu i širinu letvica (šipki) u cm, kao i gustinu drveta u kg/m³. Ako korisnik ne unese vrednosti, koristiće se podrazumevane vrednosti.
- Program traži od korisnika da unese
- Analiza i grupisanje dužina ivica:
- Program učitava model iz STL fajla i prepoznaje sve ivice kupole.
- Zatim, grupiše ivice prema njihovoj dužini, koristeći preciznost do dve decimale. Svaka dužina je dodeljena odgovarajućoj boji iz unapred definisane palete boja.
- Na kraju, ispisuje grupisane dužine ivica sa odgovarajućim brojem pojavljivanja i boje.
- Generisanje 3D modela letvica:
- Za svaku ivicu kupole, program generiše 3D model šipke (letvice) sa odgovarajućom dužinom, visinom i širinom.
- Svaka šipka je obojena na osnovu svoje dužine.
- Eksport u PLY format:
- Svi modeli šipki se kombinuju u jedan 3D objekat i eksportuju u
.plyfajl, koji sadrži geometriju i boje šipki. - Ime fajla PLY izlaza se bazira na imenu ulaznog STL fajla, sa dodatkom “_montaza.ply”.
- Svi modeli šipki se kombinuju u jedan 3D objekat i eksportuju u
- Statistika konstrukcije:
- Program broji čvorišta (kružne tačke gde se susreću letvice) i prikazuje broj čvorišta sa 3, 4, 5 i 6 letvica.
- Takođe se izračunavaju statistike kao što su ukupna dužina letvica, volumen, površina kupole, zapremina kupole i masa konstrukcije.
- Izveštaj u TXT fajlu:
- Program generiše izveštaj u .txt fajlu, koji sadrži sve relevantne informacije o konstrukciji: broj čvorišta, ukupnu dužinu letvica, volumen, površinu kupole i masu, kao i naziv PLY fajla u kojem je sačuvan 3D model letvica.
Instalacija svih potrebnih zavisnosti:
python3 -m pip install numpy trimesh
Programski kod za rev.py
#rev.py
# Instalacija potrebnih zavisnosti:python3 -m pip install numpy trimesh
# 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
import csv
from collections import Counter, defaultdict
import sys
import os
report_lines = []
report_lines.append("\n Izveštaj:")
if len(sys.argv) < 2:
print("Moraš navesti .stl fajl kao argument! Primer:\npython analiza_kupole.py ime_fajla.stl")
sys.exit(1)
stl_file = sys.argv[1]
if not os.path.isfile(stl_file):
print(f" Fajl {stl_file} ne postoji!")
sys.exit(1)
# Učitavanje STL fajla
dome = trimesh.load_mesh(stl_file)
report_lines.append(f"Učitan je .stl fajl: {stl_file}")
# Traženje visine i širine letvice
try:
stick_height = float(input("Unesi visinu letvice u cm (npr. 5): ") or 5)
except ValueError:
stick_height = 5
report_lines.append(f"Visina letvice: {stick_height} cm")
try:
stick_width = float(input("Unesi širinu letvice u cm (npr. 3): ") or 3)
except ValueError:
stick_width = 3
report_lines.append(f"Širina letvice: {stick_width} cm")
# Traženje gustine drveta
try:
gustina_kg_m3 = float(input("Unesi gustinu drveta u kg/m³ (npr. 500): ") or 500)
except ValueError:
gustina_kg_m3 = 500
report_lines.append(f"Gustinu drveta: {gustina_kg_m3} kg/m³")
# Bazni naziv fajla
file_base = os.path.splitext(os.path.basename(stl_file))[0]
# Pronalazak ivica
edges = dome.edges_unique
edge_lengths = dome.edges_unique_length
# Grupisanje dužina
rounded_lengths = [round(l, 2) for l in edge_lengths] # Ispravljeno na 2 decimale
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
# Ispis grupisanih dužina
print("\n Grupisane dužine ivica:")
report_lines.append("\n Grupisane dužine letvica:")
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:.2f} cm -> Broj letvica: {count}") # Ispis na 2 decimale
report_lines.append(f"{color_name}: {length:.2f} cm -> Broj letvica: {count} kom.")
# Generisanje 3D modela šipki sa bojama
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, 2) # Ispravljeno na 2 decimale
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
if boxes:
all_sticks = trimesh.util.concatenate(boxes)
ply_file = f"{file_base}_montaza.ply"
all_sticks.export(ply_file)
print(f"\n Montaža letvica sačuvana kao: {ply_file}")
report_lines.append(f"\n Montaža letvica sačuvana kao: {ply_file}")
# Brojanje čvorišta
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("\n Čvorišta ")
print(f" Čvorišta sa 3 letvice: {broj_3} kom.")
print(f" Čvorišta sa 4 letvice: {broj_4} kom.")
print(f" Čvorišta sa 5 letvica: {broj_5} kom.")
print(f" Čvorišta sa 6 letvica: {broj_6} kom.")
report_lines.append(f"\n Čvorišta ")
report_lines.append(f" Čvorišta sa 3 letvice: {broj_3} kom.")
report_lines.append(f" Čvorišta sa 4 letvice: {broj_4} kom.")
report_lines.append(f" Čvorišta sa 5 letvice: {broj_5} kom.")
report_lines.append(f" Čvorišta sa 6 letvice: {broj_6} kom.")
# Statistika konstrukcije
ukupna_duzina_cm = sum(edge_lengths)
ukupna_duzina_m = ukupna_duzina_cm / 100
volumen_m3 = ukupna_duzina_m * (stick_width / 100) * (stick_height / 100)
povrsina_cm2 = dome.area
povrsina_m2 = povrsina_cm2 / 10000
# Procena zapremine kupole (sferni isečak)
xy_coords = dome.vertices[:, :2]
udaljenosti_od_centra = np.linalg.norm(xy_coords, axis=1)
efektivni_poluprecnik = np.max(udaljenosti_od_centra) / 100
h = (max(dome.vertices[:, 2]) - min(dome.vertices[:, 2])) / 100
zapremina_m3 = (1/3) * np.pi * h**2 * (3 * efektivni_poluprecnik - h)
pokrivena_povrsina_m2 = np.pi * efektivni_poluprecnik**2
masa_kg = volumen_m3 * gustina_kg_m3
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")
report_lines.append(f"\n Statistika konstrukcije:")
report_lines.append(f" Ukupna dužina letvica: {ukupna_duzina_m:.2f} m")
report_lines.append(f" Kubikaža letvica (za {stick_width}x{stick_height} cm): {volumen_m3:.3f} m³")
report_lines.append(f" Površina kupole: {povrsina_m2:.2f} m²")
report_lines.append(f" Zapremina kupole: {zapremina_m3:.2f} m³")
report_lines.append(f" Površina koju kupola pokriva na tlu: {pokrivena_povrsina_m2:.2f} m²")
report_lines.append(f" Težina drvene konstrukcije: {masa_kg:.2f} kg")
# Spremanje izveštaja u .txt fajl
txt_file = f"{file_base}_izvestaj.txt"
with open(txt_file, 'w') as f:
f.write("\n".join(report_lines))
print(f"\n Izveštaj sačuvan kao: {txt_file}")
Primer upotrebe programa rev.py:

