Interaktivni Program za Pregled i Merenje crteža u DXF formatu

Ovaj Python program omogućava korisnicima interaktivni rad sa DXF fajlovima direktno sa komandne linije. Program je razvijen korišćenjem ezdxf i matplotlib biblioteka i podržava sledeće ključne funkcionalnosti:

1. Pan (Pomicanje Crteža)

  • Pritiskom na točkić miša korisnici mogu da pomeraju crtež na ekranu. Ova funkcionalnost omogućava lakšu navigaciju kroz velike crteže, čineći ih jednostavno preglednim i prilagodljivim.

2. Zumiranje

  • Okretanjem točkića miša korisnici mogu da zumiraju crtež, povećavajući ili smanjujući prikaz crteža u određenim područjima, što omogućava detaljan pregled elemenata crteža.

3. Merenje Rastojanja između Dve Tačke

  • Pritiskom levog tastera miša na dve tačke, korisnici mogu da izmere rastojanje između tih tačaka.
  • Program izračunava euklidsku distancu i prikazuje rezultat u realnom vremenu.
  • Svi rezultati merenja (rastojanje) se ispisuju u konzoli.

4. Merenje Površine između VIše Tačaka

  • Desnim klikom birate više tačaka za formiranje poligona. 
  • Kliknite ponovo na prvu tačku da zatvorite poligon. 
  • Rezultat će biti prikazan u konzoli..

5. Merenje Ugla između Tri Tačke

  • Dok držite pritisnut taster CTRL 
  • Pritiskom na desni taster miša na tri tačke,
  • izmerićete ugao između tih tačaka.
  • Program prikazuje ugao u stepenima.
  • Svi rezultati merenja (ugao) se ispisuju u konzoli.

Naravno! Evo pregleda šta sve program može da iscrta iz DXF fajla, na osnovu trenutne implementacije:


✏️ Entiteti koje program prikazuje

Program koristi biblioteku ezdxf za parsiranje DXF fajla i matplotlib za crtanje. U okviru draw_dxf() funkcije, podržani su sledeći entiteti:


1. LINE

🔹 Opis: Pravolinijski segment između dve tačke.
🔹 Prikazano kao: Plava linija
🔹 Kod:

ax.plot([start.x, end.x], [start.y, end.y], 'b')

2. CIRCLE

🔹 Opis: Krug definisan centrom i radijusom.
🔹 Prikazano kao: Crveni krug (bez ispune)
🔹 Kod:

c = plt.Circle((center.x, center.y), radius, color='r', fill=False)
ax.add_patch(c)

3. LWPOLYLINE

🔹 Opis: Lakša verzija polilinije — niz povezanih tačaka.
🔹 Prikazano kao: Magenta linije
🔹 Kod:

ax.plot(x_vals, y_vals, 'm')

4. POLYLINE

🔹 Opis: Klasična polilinija, često sa dodatnim osobinama (3D, visina, itd.)
🔹 Prikazano kao: Cijan (svetloplava) linija
🔹 Kod:

ax.plot(x_vals, y_vals, 'c')

📐 Napomena

  • Slojevi (Layers): Program prikazuje sve entitete bez obzira na sloj.
  • Zatvorene konture: Kod POLYLINE i LWPOLYLINE, čak i ako su zatvorene, prikazuju se kao obične linije — ne popunjavaju se automatski.

❌ Entiteti koje program ne podržava (još)

Trenutno se ignorišu sledeći elementi (ali ih je moguće dodati):

  • ARC (luk)
  • ELLIPSE (elipsa)
  • TEXT, MTEXT (tekstualni elementi)
  • SPLINE (krivolinijski oblici)
  • HATCH (šrafura/popunjenost)
  • DIMENSION (dimenzije)
  • BLOCKS (blokovi i referencirane instance)

Ako želište da dodate podršku za još neki od ovih elemenata — možete proširiti funkciju draw_dxf() da ih prepozna i iscrta!

Pokretanje Programa u Konzoli

✅ 1. Zahtevi (Zavisnosti)

Pre nego što pokreneš program, uveri se da su instalirani sledeći paketi:

  • Python 3 (verzija 3.6+)
  • ezdxf – za rad sa DXF fajlovima
  • matplotlib – za crtanje i prikaz grafike

🔧 Instalacija paketa

U terminalu pokreni sledeće komande:

sudo apt update
sudo apt install python3 python3-pip -y
pip3 install ezdxf matplotlib

✅ 2. Smeštanje programa

  1. Otvori terminal.
  2. Kreiraj novi folder (ako želiš):
mkdir ~/vidi_dxf
cd ~/vidi_dxf

Kreiraj novi Python fajl:

nano xdxf.py

(Zalepi ceo Python kod i sačuvaj: CTRL + O, zatim ENTER, pa CTRL + X za izlaz.)

Python kod koji kopiraš u xdxf.py

import ezdxf
import matplotlib.pyplot as plt
import sys
import os
import math

clicked_points = []
point_markers = []
polygon_patch = None
drag_start = None
is_panning = False

def distance(p1, p2):
    return math.hypot(p2[0] - p1[0], p2[1] - p1[1])

def polygon_area(points):
    n = len(points)
    area = 0.0
    for i in range(n):
        x1, y1 = points[i]
        x2, y2 = points[(i + 1) % n]
        area += x1 * y2 - x2 * y1
    return abs(area) / 2.0

def draw_point(x, y):
    marker, = ax.plot(x, y, 'ko', markersize=5)
    point_markers.append(marker)
    fig.canvas.draw()

def draw_polygon(points):
    global polygon_patch
    if polygon_patch:
        polygon_patch.remove()

    polygon_patch = plt.Polygon(points, closed=True, edgecolor='orange', facecolor='orange', alpha=0.3)
    ax.add_patch(polygon_patch)
    fig.canvas.draw()

def clear_visuals():
    global point_markers, polygon_patch
    for m in point_markers:
        m.remove()
    point_markers.clear()

    if polygon_patch:
        polygon_patch.remove()
        polygon_patch = None

    fig.canvas.draw()

def on_click(event):
    global is_panning, drag_start
    if event.inaxes:
        if event.button == 1:  # Levim klikom meri rastojanje
            x, y = event.xdata, event.ydata
            clicked_points.append((x, y))
            print(f"Kliknuta tačka: ({x:.2f}, {y:.2f})")
            draw_point(x, y)

            if len(clicked_points) == 2:
                d = distance(clicked_points[0], clicked_points[1])
                print(f"Rastojanje između tačaka: {d:.2f} jedinica\n")
                clicked_points.clear()
                clear_visuals()

        elif event.button == 2:  # Srednji klik (točkić) za pan
            is_panning = True
            drag_start = (event.xdata, event.ydata)

        elif event.button == 3:  # Desni klik za merenje površine ili ugla
            x, y = event.xdata, event.ydata

            # Detekcija CTRL + 3 desna klika za merenje ugla
            if event.key == 'control':
                clicked_points.append((x, y))
                draw_point(x, y)
                if len(clicked_points) == 3:
                    a, b, c = clicked_points
                    angle = math.degrees(math.atan2(c[1] - b[1], c[0] - b[0]) -
                                         math.atan2(a[1] - b[1], a[0] - b[0]))
                    angle = abs(angle)
                    if angle > 180:
                        angle = 360 - angle
                    print(f"Ugao između tri tačke: {angle:.2f} stepeni\n")
                    clicked_points.clear()
                    clear_visuals()
                return

            new_point = (x, y)

            if len(clicked_points) >= 3:
                first_point = clicked_points[0]
                if distance(first_point, new_point) < 5.0:
                    draw_polygon(clicked_points)
                    area = polygon_area(clicked_points)
                    print(f"Površina zatvorenog poligona ({len(clicked_points)} temena): {area:.2f} kv. jedinica\n")
                    clicked_points.clear()
                    clear_visuals()
                    return

            clicked_points.append(new_point)
            draw_point(x, y)
            print(f"Kliknuta tačka za površinu: ({x:.2f}, {y:.2f})")

def on_motion(event):
    global drag_start
    if event.inaxes and is_panning:
        dx = event.xdata - drag_start[0]
        dy = event.ydata - drag_start[1]
        ax.set_xlim(ax.get_xlim() - dx)
        ax.set_ylim(ax.get_ylim() - dy)
        drag_start = (event.xdata, event.ydata)
        fig.canvas.draw()

def on_release(event):
    global is_panning
    if event.button == 2:
        is_panning = False

def on_scroll(event):
    global ax
    if event.inaxes:
        scale_factor = 1.1 if event.button == 'up' else 0.9
        xlim, ylim = ax.get_xlim(), ax.get_ylim()
        center_x, center_y = (xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2
        ax.set_xlim(center_x - (center_x - xlim[0]) * scale_factor, center_x + (xlim[1] - center_x) * scale_factor)
        ax.set_ylim(center_y - (center_y - ylim[0]) * scale_factor, center_y + (ylim[1] - center_y) * scale_factor)
        fig.canvas.draw()

def on_key(event):
    if event.key == 'escape':
        print("⛔ ESC pritisnut – prekid merenja i čišćenje tačaka.\n")
        clicked_points.clear()
        clear_visuals()

def draw_dxf(file_path):
    print(f"Učitavanje DXF fajla: {file_path}")

    try:
        doc = ezdxf.readfile(file_path)
    except IOError:
        print(f"❌ Greška: Ne mogu da otvorim fajl '{file_path}'")
        return
    except ezdxf.DXFStructureError:
        print(f"❌ Greška: Fajl '{file_path}' nije validan DXF fajl")
        return

    msp = doc.modelspace()
    global fig, ax
    fig, ax = plt.subplots()

    for entity in msp:
        if entity.dxftype() == 'LINE':
            start = entity.dxf.start
            end = entity.dxf.end
            ax.plot([start.x, end.x], [start.y, end.y], 'b')

        elif entity.dxftype() == 'CIRCLE':
            center = entity.dxf.center
            radius = entity.dxf.radius
            c = plt.Circle((center.x, center.y), radius, color='r', fill=False)
            ax.add_patch(c)

        elif entity.dxftype() == 'LWPOLYLINE':
            points = [point[:2] for point in entity]
            if len(points) > 1:
                x_vals, y_vals = zip(*points)
                ax.plot(x_vals, y_vals, 'm')

        elif entity.dxftype() == 'POLYLINE':
            points = [v.dxf.location for v in entity.vertices()]
            if len(points) > 1:
                x_vals = [p.x for p in points]
                y_vals = [p.y for p in points]
                ax.plot(x_vals, y_vals, 'c')

    ax.set_aspect('equal')
    ax.autoscale()
    ax.set_title("Levi klik:rastojanje|Desni klik:površina|CTRL+Desni klik:ugao|Zum:točkić|Pan:srednji klik")
    ax.set_xlabel("X")
    ax.set_ylabel("Y")
    ax.grid(True)

    fig.canvas.mpl_connect("button_press_event", on_click)
    fig.canvas.mpl_connect("motion_notify_event", on_motion)
    fig.canvas.mpl_connect("button_release_event", on_release)
    fig.canvas.mpl_connect("scroll_event", on_scroll)
    fig.canvas.mpl_connect("key_press_event", on_key)

    try:
        plt.tight_layout()
        plt.show()
    except Exception as e:
        print(f"❌ Greška pri prikazu crteža: {e}")
        input("Pritisni Enter za izlaz...")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Upotreba: python vidi_dxf.py <putanja_do_dxf_fajla>")
        sys.exit(1)

    print("""
  - Merenje rastojanja: Pritiskom na levi taster miša na dve tačke, 
    izmerićete rastojanje između tih tačaka. 
    Rezultat će biti prikazan u konzoli.
  - Merenje površine: Desnim klikom birate više tačaka za formiranje poligona. 
    Kliknite ponovo na prvu tačku da zatvorite poligon. 
    Rezultat će biti prikazan u konzoli.
  - Pan (Pomicanje crteža): Pritiskom na srednji taster miša (točkić), 
    možete slobodno pomerati crtež na ekranu
  - Zumiranje: Okretanjem točkića miša, možete povećavati 
    ili smanjivati prikaz crteža.
  - Merenje ugla: Dok držite pritisnut taster CTRL 
    Pritiskom na desni taster miša na tri tačke,
    izmerićete ugao između tih tačaka. Rezultat će biti prikazan u konzoli.
  - ESC taster prekida sva merenja i čisti ekran od mernih tačaka.
""")

    file_path = sys.argv[1]

    if not os.path.isfile(file_path):
        print(f"❌ Fajl '{file_path}' ne postoji!")
        sys.exit(1)

    draw_dxf(file_path)
    print("✅ Program završen.")

3. Priprema DXF fajla

U isti folder kopiraj .dxf fajl koji želiš da otvoriš.

Primer (ako se fajl zove primer.dxf):

cp /putanja/do/primer.dxf ~/vidi_dxf/

✅ 4. Pokretanje programa

Pokreni program iz terminala na sledeći način:

python3 xdxf.py primer.dxf

🧭 5. Uputstvo za korišćenje programa

Kada pokreneš program, u terminalu će se prikazati sledeće uputstvo:

  - Merenje rastojanja: Pritiskom na levi taster miša na dve tačke, 
    izmerićete rastojanje između tih tačaka. 
    Rezultat će biti prikazan u konzoli.
  - Merenje površine: Desnim klikom birate više tačaka za formiranje poligona. 
    Kliknite ponovo na prvu tačku da zatvorite poligon. 
    Rezultat će biti prikazan u konzoli.
  - Pan (Pomicanje crteža): Pritiskom na srednji taster miša (točkić), 
    možete slobodno pomerati crtež na ekranu
  - Zumiranje: Okretanjem točkića miša, možete povećavati ili smanjivati prikaz crteža.
  - Merenje ugla: Dok držite pritisnut taster CTRL Pritiskom na desni taster miša na tri tačke,
    izmerićete ugao između tih tačaka. Rezultat će biti prikazan u konzoli.
  - ESC taster prekida sva merenja i čisti ekran od mernih tačaka.

🖱️ Pregled funkcionalnosti

RadnjaOpis
Levi klik (x2)Izmeri rastojanje između dve tačke.
Desni klik (više puta)Formira poligon, zatvara ga klikom blizu prve tačke.
CTRL + 3x desni klikIzračunava ugao između tri tačke.
Srednji klik (točkić)Panoramsko pomeranje crteža.
Točkić mišaZumiranje u/van.

🛑 6. Zatvaranje programa

Klikom na X u prozoru ili zatvaranjem matplotlib prikaza – program se završava.


Primer Pokretanja Programa:

Na primer, ako želite da otvorite fajl kupola.dxf koji se nalazi u istom direktorijumu kao i skripta, komanda bi izgledala ovako:

python3 xdxf.py kupola.dxf

U prozoru koji se otvori, možete interaktivno pomerati crtež, zumirati, meriti rastojanja, površine i uglove.

Ovaj crtež je rađen u takvoj razmeri da su rezultati merenja dužine u santimetrima. Svi rezultati merenja (rastojanje i ugao) biće prikazani u konzoli, kao na primer:

Ovaj način interakcije omogućava jednostavno i efikasno radno okruženje u komandnoj liniji, bez potrebe za dodatnim grafičkim korisničkim interfejsima. Program je brz, fleksibilan i idealan za rad sa jednostavnim CAD crtežima i merenja u DXF formatu.

By Abel

Leave a Reply

Your email address will not be published. Required fields are marked *