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.
# 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("\n📏 Grupisane 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
