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
.stl
fajl 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
.ply
fajl, 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.
# 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:
