;;; ==================================================================
;;;  FiberQ CAD  --  fq-kucista.lsp
;;;  Kucista opticke mreze: ODF, Patch panel, ODO ormar, MDU ormar
;;;  i ZOK (zavrsna opticka kutija).
;;;
;;;  Ucitava se POSLE fq-sema.lsp.  Ne menja nista postojece, samo
;;;  dodaje.  Stare komande FQODF i FQODO rade nepromenjeno.
;;;
;;;  TRI NACINA RADA, isti kao u ostatku AutoCAD-a
;;;    1. dugme na traci    crta odmah, bez pitanja, po katalogu
;;;    2. FQKUC             zajednicki dijalog za sva kucista
;;;    3. -FQZOK i slicno   niz pitanja u komandnoj liniji, za skripte
;;;
;;;  Sve tri grane zavrsavaju u istoj funkciji fq:kucdraw, pa se
;;;  crtez ne moze razlikovati po tome kako je pokrenut.
;;;
;;;  Copyright (c) 2026 Vladimir Vukovic
;;;  MIT licenca, ceo tekst je u fajlu LICENSE pored ovoga.
;;; ==================================================================

(vl-load-com)

;;  Bezuslovno, isto kao u fq-sema.lsp -- vidi tamo zasto nije uslovno.
(setq *FQ-VERZIJA* "1.0.0")


;;; ------------------------------------------------------------------
;;;  1. KATALOG KUCISTA
;;;
;;;  Jedan zapis:
;;;    (kljuc naziv crtac prefiks (kapaciteti) (modeli) (montaze) splmin)
;;;
;;;  crtac    "odf"  ram sa konektorima, letva i modul      (ODF, PP)
;;;           "odo"  ormar sa portovima i pregradom         (ODO, MDU)
;;;           "zok"  kutija, po potrebi sa spliterom unutra (ZOK)
;;;  prefiks  ide ispred oznake u crtezu
;;;  splmin   najmanji kapacitet na kome je spliter u kutiji dozvoljen,
;;;           0 znaci da ga ovo kuciste nikad nema
;;;
;;;  Katalog se cuva u fq-kucista.dat komandom FQKATSAVE, isto kao sto
;;;  se color code standardi cuvaju u fq-boje.dat komandom FQCC.
;;; ------------------------------------------------------------------

;;  Oznake konektora su opisi konstrukcije (SC, LC, FC, ST, LSH), a ne
;;  imena proizvoda.  LSH je uobicajena neutralna oznaka za konektor
;;  koji se u katalozima jednog proizvodjaca vodi pod zasticenim imenom;
;;  ko hoce bas to ime, upise ga -- lista nije zakljucana.
(setq *FQ-KONEKTORI*
  '("SC/APC" "SC/UPC" "LC/APC" "LC/UPC" "LSH/APC" "FC/PC" "ST/PC"))

;;  MODELI SE NE ISPORUCUJU.
;;
;;  Model je proizvodjac i tip -- ono sto stoji u specifikaciji.  Do
;;  verzije 1.0 su ovde stajala imena stvarnih proizvodjaca, i to je
;;  bilo pogresno iz dva razloga.  Pravni je ocigledan: tudji zastitni
;;  znak u isporucenom proizvodu.  Drugi je gori, jer radi tiho:
;;  fq:kc-zmod vraca PRVI clan liste, a dugme na traci ne pita nista,
;;  pa je svako ko klikne "ODO 48" dobijao ime prvog proizvodjaca sa
;;  spiska upisano i u crtez i u xdata i u specifikaciju -- bez ijednog
;;  svesnog izbora.
;;
;;  Zato je lista prazna, a model se upisuje u polje "Model" u dijalogu
;;  kucista.  Ono sto upises pamti se za tu vrstu do kraja sesije i
;;  dopisuje se u ovaj katalog, pa ti sledeci put stoji u padajucoj
;;  listi.  FQKATSAVE ga trajno cuva u tvom profilu.
(setq *FQ-KUCISTA*
 '(("ODF" "ODF modul" "odf" "OR "
     (12 24 48 72 96 144 288 576)
     ()
     ("rack 19\"")
     0)

   ("PP" "Patch panel" "odf" "PP "
     (12 24 48 72 96 144)
     ()
     ("rack 19\"")
     0)

   ("ODO" "ODO ormar" "odo" "ODO "
     (12 24 48 72 96)
     ()
     ("unutrasnji" "spoljasnji" "samostojeci")
     12)

   ;;  MDU = Multi-Dwelling Unit, ormar u stambenoj zgradi.  Do verzije
   ;;  1.0 se zvao po imenu jedne proizvodjacke serije.  Kljuc "IPAN"
   ;;  NAMERNO ostaje: on stoji u xdata svakog vec nacrtanog ormara, u
   ;;  fq:kucpolja, fq:kucprepis i u specifikaciji.  Menja se samo ono
   ;;  sto korisnik vidi.
   ("IPAN" "MDU ormar" "odo" "MDU "
     (12 24 48 72 96 144)
     ()
     ("unutrasnji" "samostojeci")
     12)

   ("ZOK" "ZOK kutija" "zok" "ZOK "
     (1 2 4 6 8 12 16 24)
     ()
     ("zid" "stub" "unutrasnji")
     12)))

;; FQ_ZOK je od 1.0.0 u *FQ-LAYERS* u fq-sema.lsp, tamo gde mu je i
;; mesto.  Ovo dopisivanje ostaje kao pojas za slucaj da neko ucita
;; STARIJI fq-sema.lsp uz ovaj fajl -- tada FQSTIL i dalje pokriva ZOK.
;; Sa novim fq-sema.lsp uslov je netacan i ne radi nista.
(if (and (boundp '*FQ-LAYERS*) (not (assoc "FQ_ZOK" *FQ-LAYERS*)))
  (setq *FQ-LAYERS*
    (append *FQ-LAYERS*
            (list (list "FQ_ZOK" 4 25 "ZOK kutija" "FAT box")))))

;; Isti pojas, drugi razlog i tezi ishod.  *FQ-ODO-TIT1* i *FQ-ODO-TIT2*
;; su uvedeni 23.08.2026 u fq-sema.lsp; ovaj fajl ih cita u
;; fq:drawodospl i u fq:kucproz.  Mesan par -- nov fq-kucista.lsp uz
;; STARIJI fq-sema.lsp -- nije teorijski, to je korisnik koji je
;; prekopirao jedan fajl.
;;
;; A pada prljavo: (+ vt nil) u fq:drawodospl puca POSLE fq:Wrect i
;; fq:xd, pa u crtezu ostane ram sa zapisom, bez ijednog natpisa i bez
;; portova -- ormar koga specifikacija broji a na papiru ga nema.  U
;; fq:kucproz bi (< gore nil) puknuo samo kad sused POSTOJI, dakle
;; povremeno i bas u gustom crtezu.
;;
;; boundp, ne go setq: noviji fq-sema.lsp ucitan posle prepise istom
;; vrednoscu, pa se nista ne razilazi.
(if (not (boundp '*FQ-ODO-TIT1*)) (setq *FQ-ODO-TIT1* 3.10))
(if (not (boundp '*FQ-ODO-TIT2*)) (setq *FQ-ODO-TIT2* 1.50))

;;; --- mere ZOK kutije, u istom merilu kao ODF ----------------------
(setq *FQ-ZOK-DX*    1.00)   ; pomeraj desne ivice od kolone konektora
(setq *FQ-ZOK-W*     6.20)   ; sirina kutije bez splitera
(setq *FQ-ZOK-WS*   10.80)   ; sirina kutije sa spliterom unutra
                             ; = *FQ-ZOK-W* + *FQ-ZOK-SPD* + *FQ-ZOK-SPW*
(setq *FQ-ZOK-MARG*  1.50)   ; margina iznad i ispod konektora
(setq *FQ-ZOK-TITH*  1.40)   ; visina naslova
(setq *FQ-ZOK-KS*    0.438)  ; razmera simbola konektora, ista kao ODF
(setq *FQ-ZOK-NUMX*  1.50)   ; pomeraj rednog broja konektora
;;  Telo splitera u kutiji drzi korak sa *FQ-SPL-W* i *FQ-SPL-H* iz
;;  fq-sema.lsp, ali se NE cita odatle: ovo su izrazi na nivou fajla i
;;  racunaju se pri ucitavanju, a fq-kucista.lsp sme da bude ucitan pre
;;  fq-sema.lsp (APPLOAD ide po abecedi, "fq-kucista" < "fq-sema").
;;  Citanje bi tada dalo nil, zbir bi pukao i NIJEDAN defun ispod ove
;;  linije se ne bi definisao.  Zato broj, a ne referenca; ako se mere
;;  u fq-sema.lsp promene, promeni i ove dve.
(setq *FQ-ZOK-SPW*   2.00)   ; sirina tela splitera, kao *FQ-SPL-W*
(setq *FQ-ZOK-SPH*   1.60)   ; visina tela splitera, kao *FQ-SPL-H*
(setq *FQ-ZOK-SPD*   2.60)   ; razmak tela splitera od kolone konektora
;;  Natpisi NA SPLITERU idu po ovome, ne po *FQ-ZOK-TITH*.  Kutija je
;;  velika i njen naslov od 1.40 stoji, ali telo splitera je siroko samo
;;  2.00: naslov "1:16" na 0.62 * 1.40 = 0.87 visine izlazi iz tela na
;;  obe strane.  Isto kao *FQ-SPL-TITH* u fq-sema.lsp, opet brojem a ne
;;  referencom, iz istog razloga kao SPW i SPH iznad.
(setq *FQ-ZOK-SPTITH* 0.90)  ; visina natpisa na telu splitera

;;; --- spliter u ODO ormaru -----------------------------------------
;;;
;;;  Telo splitera stoji izmedju pregrade ormara i kolone portova.
;;;  ODO je *FQ-ODO-W* = 12 sirok, pregrada je *FQ-ODO-LEV* = 2.8 od
;;;  leve ivice, a simbol porta zauzima *FQ-ODO-PW* = 1.83 od kolone
;;;  nalevo, pa telo od *FQ-SPL-W* staje izmedju njih sa zazorom na obe
;;;  strane.  Broj se namerno ne prepisuje: telo je vec dvaput smanjeno
;;;  (6.0 -> 2.6 -> 2.0), a zastareo broj u ovom komentaru vodi na
;;;  pogresan zakljucak bas pri sledecoj promeni mera.
;;;  Same mere tela idu iz *FQ-SPL-...* u fq-sema.lsp, iste one po
;;;  kojima se crta i samostalan spliter.
(setq *FQ-ODO-SPD*   2.60)   ; razmak tela splitera od kolone portova

;; Dugme na traci ne zna koliko vlakana ide na konektore, pa je do sada
;; uzimalo sve sto kabl ima.  Sada se to jednom pita u komandnoj liniji,
;; a Enter zadrzava staro ponasanje.  Ko hoce da pitanja nema, postavi
;; ovo na nil; dijalog i -FQODO i dalje pitaju svoje.
(setq *FQ-PITAJ-VL* T)

;;; --- zapamcen izbor, da dugme na traci ne pita nista --------------
(setq *fq-kuc-tip* "ODF")
(setq *fq-kuc-kon* "SC/APC")
;;  Prazno namerno.  Do 23.08.2026 je stajalo "L1, M1", pa je svaki
;;  razdelnik i patch panel polozen dugmetom nosio oznaku letve koju
;;  niko nije izabrao -- isto pravilo koje vec vazi za model kucista:
;;  ono sto korisnik nije rekao ne sme da zavrsi u crtezu ni u
;;  specifikaciji.  U praksi se tu upisuje oznaka rek ormana i mesto
;;  montaze ("rack 19\", sprat 2"), a to se ne moze pogoditi.
;;
;;  Prazno prolazi kroz ceo put: natpis se ne crta (prazan fq:Wtext bi
;;  napravio nevidljiv TEXT koji ssget svejedno hvata), a veza vlakna
;;  ide kroz fq:spoj, pa bude "PP 1/3" umesto "PP 1 /3".  Kod vise
;;  modula prazna oznaka postaje "M1", "M2" -- moduli moraju da se
;;  razlikuju, jer se bas po toj niski citaju nazad (fq:kucvez).
(setq *fq-kuc-lm*  "")
(setq *fq-kuc-nm*  1)        ; broj modula jednog razdelnika, ODF i PP
(setq *fq-kuc-adr* "")
(setq *fq-kuc-spl* 8)
(setq *fq-kuc-kap* nil)      ; popunjava se iz kataloga pri prvom otvaranju
(setq *fq-kuc-mod* nil)      ; asocijativna lista (tip . model)
(setq *fq-kuc-mnt* nil)      ; asocijativna lista (tip . montaza)

;; Najveci standardni odnos splitera koji staje u dati kapacitet.
;; Jedan konektor odlazi na napajanje splitera, pa izlazima ostaje
;; kap-1: ZOK 12 tako dobija 1:8 (izlazi 1-8, slobodni 9-11, napojni
;; 12), ZOK 16 takodje 1:8, a ZOK 24 dobija 1:16.
(setq *FQ-SPL-ODNOSI* '(2 4 8 16 32 64))

(defun fq:splfit (kap / r)
  (setq r (car *FQ-SPL-ODNOSI*))
  (foreach s *FQ-SPL-ODNOSI* (if (<= s (1- kap)) (setq r s)))
  r)


;;; ------------------------------------------------------------------
;;;  2. CITANJE KATALOGA
;;; ------------------------------------------------------------------

(defun fq:kc     (tip) (assoc (strcase tip) *FQ-KUCISTA*))

;;  SVAKO polje kataloga ide kroz OVU funkciju, i to zbog jedne
;;  osobine AutoLISP-a: (nth n nil) je GRESKA, a ne nil.
;;
;;  Do 25.08.2026 je svaki citac glasio (nth 1 (fq:kc tip)).  Za vrstu
;;  koje u katalogu nema fq:kc vraca nil, pa je citac pucao -- i to je
;;  obaralo FQINFO nad JEDANAEST od sesnaest vrsta zapisa: vlakno,
;;  varenje, nastavak, spliter, ulaz, izlaz, OLT, ONT, PON, privod,
;;  rezerva.  Komanda je javljala "bad argument type: consp nil" i nije
;;  ispisivala nista.
;;
;;  Namera je od pocetka bila suprotna, i tako pise u komentaru uz
;;  fq:vrnaziv: "ostalih jedanaest fq:vrime ne poznaje i vraca ''".
;;  Greska nije bila u nameri nego u pretpostavci da se nth nad nil
;;  ponasa kao car nad nil.  Ne ponasa se.
;;
;;  Isti nil pokriva i slucaj kada fq-kucista.lsp uopste nije ucitan,
;;  pa jedna grana i dalje pokriva oba -- kako je i bilo zamisljeno.
(defun fq:kc-f (tip n / r) (if (setq r (fq:kc tip)) (nth n r)))

(defun fq:kc-ime (tip) (fq:kc-f tip 1))
(defun fq:kc-crt (tip) (fq:kc-f tip 2))
(defun fq:kc-pre (tip) (fq:kc-f tip 3))
(defun fq:kc-kap (tip) (fq:kc-f tip 4))
(defun fq:kc-mod (tip) (fq:kc-f tip 5))
(defun fq:kc-mnt (tip) (fq:kc-f tip 6))
(defun fq:kc-spm (tip) (fq:kc-f tip 7))

;; Da li ovaj tip i kapacitet uopste mogu da nose spliter u kutiji.
(defun fq:kc-splok (tip kap / m)
  (setq m (fq:kc-spm tip))
  (and m (> m 0) (>= kap m)))

;; Zapamcena vrednost po tipu, sa razumnim prvim izborom iz kataloga.
;;
;; fq:kc-zmod mora da vrati STRING i kad model nije poznat.  Isporuceni
;; katalog nema nijedan model (vidi zasto gore), pa je (car nil) = nil,
;; a nil u strcat prekida crtanje kucista.  Zato prazan string na kraju.
(defun fq:kc-zmod (tip / r)
  (cond ((setq r (assoc tip *fq-kuc-mod*)) (cdr r))
        ((car (fq:kc-mod tip)))
        (T "")))
(defun fq:kc-zmnt (tip / r)
  (cond ((setq r (assoc tip *fq-kuc-mnt*)) (cdr r))
        ((car (fq:kc-mnt tip)))
        (T "")))

;; Dopisuje upisan model u katalog te vrste, da sledeci put stoji u
;; padajucoj listi i da ga FQKATSAVE sacuva.  Prazan i vec postojeci
;; se preskacu; poredjenje je bez obzira na velicinu slova, jer bi
;; inace "Marka" i "MARKA" bila dva zapisa u istoj listi.
;;
;; ZAPIS SE MENJA, NE SKLAPA PONOVO.  Do 1.0.0 je ovde stajao (append
;; (list (nth 0 rec) ... (nth 4 rec) nova-lista) (list (nth 6 rec)
;; (nth 7 rec))) -- dakle TACNO osam clanova, ma koliko ih zapis imao.
;; Svaki model koji korisnik upise tiho je brisao deveto polje te vrste,
;; a c:FQKATSAVE je skracen zapis upisivao u fq-kucista.dat.
;;
;; Kvar je bio najgore vrste: u sesiji sve radi, jer skraceni zapis jos
;; nije procitan nazad.  Nestane tek pri sledecem pokretanju, i to samo
;; za vrstu za koju je model upisan.  Zato je fq:katok namerno pisan da
;; prima >= 8 clanova -- ali taj put je do sada bio neprohodan.
;;
;; fq:setnthx menja SAMO trazeno polje i prenosi sve ostalo, pa je
;; osobina "rep se ne dira" strukturna, a ne stvar pazljivog kucanja.
(defun fq:kc-addmod (tip m / rec lst)
  (if (and m (/= m "") (setq rec (fq:kc tip)))
    (progn
      (setq lst (nth 5 rec))
      (if (not (vl-some '(lambda (x) (= (strcase x) (strcase m))) lst))
        (setq *FQ-KUCISTA*
              (subst (fq:setnthx rec 5 (append lst (list m)))
                     rec *FQ-KUCISTA*)))))
  m)

(defun fq:kc-set (lst tip v / r)
  (if (setq r (assoc tip lst))
    (subst (cons tip v) r lst)
    (cons (cons tip v) lst)))

;; Redni broj vrednosti u listi, 0 ako je nema.
(defun fq:kc-poz (v lst / i r)
  (setq i 0 r 0)
  (foreach x lst
    (if (equal x v) (setq r i))
    (setq i (1+ i)))
  r)


;;; ------------------------------------------------------------------
;;;  3. ZOK, CRTANJE
;;;
;;;  Bez splitera kutija je mali ODF: konektor po vlaknu.
;;;  Sa spliterom u kutiju ulazi jedno vlakno, telo splitera deli ga
;;;  na k izlaza, a izlazi zavrsavaju na konektorima.  Telo splitera
;;;  dobija svoj xdata zapis "SPLITER", pa ga specifikacija broji kao
;;;  zasebnu stavku, a opticki budzet vidi njegovo slabljenje.
;;; ------------------------------------------------------------------

;; Zajednicko: ram, naslovi i xdata kutije.
;; oz vec nosi prefiks iz kataloga, jer ga fq:next dodaje pri
;; automatskoj oznaci.  Zato se ovde NE dodaje ponovo, inace u
;; crtezu pise "ZOK ZOK 5".
(defun fq:zokram (oz adr mod kap k kon mnt spl st ul ur vt vb cu / opis r)
  ;; Rukohvat se uzima iz same primitive, ne iz (entlast).  Bez toga bi
  ;; posle neuspelog entmake zapis "ZOK" otisao na PRETHODNI entitet u
  ;; crtezu, pa bi kutije bilo u specifikaciji a ne bi je bilo na papiru,
  ;; dok bi tudji entitet postao kuciste koje FQDODAJ ume da precrta.
  (setq r (fq:Wrect ul vt ur vb 7 "FQ_ZOK"))
  ;; Adresa je DESETO polje, dopisano 24.08.2026 na kraj.  Status je
  ;; time prestao da bude poslednji clan, pa je fq:vstat morala da dobije
  ;; izricitu granu za "ZOK" -- bez nje bi adresa postala status.  To je
  ;; postupak koji CLAUDE.md propisuje za svaku vrstu kod koje fq:vstat
  ;; pada na (fq:last d): prvo se prosiri citac, pa tek onda zapis.
  ;;
  ;; Zatecena kutija od devet polja se ne dira i ne migrira: fq:f vraca
  ;; "" za polje kog nema, pa takva kutija prosto nema adresu.
  ;; Montaza u xdata ide kao KOD, u ram kutije (dole) kao PRIKAZ.  Isti
  ;; obrazac kao u fq:drawodo i fq:drawnast; razlog je isti: mnt ovamo
  ;; stize kao naziv iz kataloga, ali i kao kod kad FQDODAJ precrtava
  ;; kutiju iz njene xdata.  Katalog zato ne mora da zna za kodove.
  (if r
    (fq:xd r (list "ZOK" oz mod (itoa kap) (itoa k) kon
                   (fq:nazkod "mnt" mnt)
                   (if spl (strcat "1:" (itoa spl)) "") st adr))
    (princ (fq:t "kuc.ram.ne")))
  ;; Adresa ide IZNAD oznake, a ne ispod nje kao kod ODO ormara.  Nije
  ;; izbor nego posledica zatecenog rasporeda: red ispod oznake kod
  ;; kutije vec drzi model i "kapacitet x konektor", a izmedju njih ostaje
  ;; 0.90 jedinica -- manje nego sto je visina samog natpisa.  Redosled
  ;; odozgo je zato adresa, oznaka, opis, ram.
  ;;
  ;; Prazna se NE crta: fq:Wtext sa "" pravi nevidljiv TEXT koji ssget
  ;; svejedno hvata, pa bi ga fq:kucbrisi merio kao natpis.
  ;; Visina dolazi iz fq:kucgornji, ne iz zakucanog broja: isti broj
  ;; citaju prozor za brisanje i pojas pomeranja natpisa, a tri kopije
  ;; se razidju tiho -- natpis ostane u crtezu ili se ne pomeri sa ramom.
  (if (/= adr "")
    (fq:Wtext cu (+ vt (fq:kucgornji "zok")) *FQ-ZOK-TITH*
              adr 7 "FQ_ZOK" "FQ_TXTB" 1 2))
  (fq:Wtext cu (+ vt (* 2.6 *FQ-ZOK-TITH*)) *FQ-ZOK-TITH*
            oz 7 "FQ_ZOK" "FQ_TXTB" 1 2)
  ;; Natpis je centriran po cu.  Da su dva razmaka bezuslovna, kutija
  ;; bez upisanog modela bi dobila "  24xSC/APC" i taj red bi vizuelno
  ;; stajao pomeren udesno u odnosu na oznaku iznad njega.
  (setq opis (strcat (if (= mod "") "" (strcat mod "  "))
                     (itoa kap) "x" kon))
  (fq:Wtext cu (+ vt (* 1.1 *FQ-ZOK-TITH*)) (* 0.72 *FQ-ZOK-TITH*)
            opis 7 "FQ_ZOK" "FQ_TXT" 1 2)
  (fq:Wtext cu (- vb (* 1.0 *FQ-ZOK-TITH*)) (* 0.72 *FQ-ZOK-TITH*)
            (fq:kodnaz "mnt" mnt) 7 "FQ_ZOK" "FQ_TXT" 1 2))

;; Telo splitera unutar kutije.
;;   vfeed  visina napojnog konektora, onog na koji dolazi kabl
;;   vizl   visine konektora na koje idu izlazi, redom 1..spl
;; Ulaz se crta kao patch od napojnog konektora do tela splitera,
;; jer u kutiji stvarno tako i ide.
(defun fq:zokspl (ozs spl st u0 vfeed vizl / g su1 su2 sv1 sv2 vmid j kl ux)
  (setq g    (fq:splgub spl)
        kl   (- u0 (* 2.11 *FQ-ZOK-KS*))      ; levi kraj simbola konektora
        vmid (/ (apply '+ vizl) (float (length vizl)))
        su2  (- u0 *FQ-ZOK-SPD*)
        su1  (- su2 *FQ-ZOK-SPW*)
        sv1  (+ vmid (* 0.5 *FQ-ZOK-SPH*))
        sv2  (- vmid (* 0.5 *FQ-ZOK-SPH*))
        ux   (- su1 1.20))
  ;; ulaz: napojni konektor -> levo -> do visine splitera -> u telo
  (fq:Wline kl vfeed ux vfeed 5 "FQ_SPLITER" nil)
  (fq:Wline ux vfeed ux vmid  5 "FQ_SPLITER" nil)
  (fq:Wline ux vmid  su1 vmid 5 "FQ_SPLITER" (list "ULAZ" ozs st))
  (fq:xd (fq:Wrect su1 sv1 su2 sv2 5 "FQ_SPLITER")
         (list "SPLITER" ozs (strcat "1:" (itoa spl)) (rtos g 2 1) st))
  (fq:Wtext (* 0.5 (+ su1 su2)) vmid (* 0.62 *FQ-ZOK-SPTITH*)
            (strcat "1:" (itoa spl)) 5 "FQ_SPLITER" "FQ_TXTB" 1 2)
  (fq:Wtext su1 (- sv2 (* 0.55 *FQ-ZOK-SPTITH*)) (* 0.55 *FQ-ZOK-SPTITH*)
            (strcat (rtos g 2 1) " dB") 5 "FQ_SPLITER" "FQ_TXT" 0 0)
  ;; lepeza: telo splitera -> levi kraj svakog izlaznog konektora
  (setq j 1)
  (foreach v vizl
    (fq:Wline su2 vmid kl v 5 "FQ_SPLITER" (list "IZLAZ" ozs (itoa j) st))
    (setq j (1+ j))))

;; fl je lista vlakana kao kod ODF-a: (redni_broj tacka ename)
;; spl je odnos splitera ili nil.
;;
;; Kutija uvek dobija SVIH kap konektora, i one na kojima jos nista ne
;; stoji, jer su oni u njoj fizicki prisutni i na njih se kasnije kaci.
;;
;; NUMERACIJA ide unazad od kapaciteta, isto pravilo koje vec vazi za
;; portove ODO ormara: prvo vlakno kabla zavrsava na POSLEDNJEM
;; konektoru.  Kod kutije sa spliterom napojno vlakno ide na konektor
;; broj kap, izlazi splitera na konektore 1..spl, a ono izmedju ostaje
;; slobodno: ZOK 12 sa spliterom 1:8 ima izlaze na 1-8, slobodne
;; konektore 9-11 i napajanje na 12.
(defun fq:drawzok (oz adr mod kap k kon mnt spl st fl ang
                   / locs u0 vin kons vizl vsvi vtop vbot no
                     ul ur vt vb cu lw i n v rec pn ozs)
  (fq:init)
  (fq:sistem ang (fq:oglsad fl ang))
  (setq locs   (mapcar '(lambda (r) (fq:loc (cadr r))) fl)
        u0     (car (car locs))
        vin    (cadr (car locs))
        kons   nil
        vizl   nil
        no     0)

  (if spl
    (progn
      ;; Napojni konektor je poslednji i stoji na visini dolaznog
      ;; vlakna, a niz raste iznad njega, pa je konektor 1 na vrhu.
      (setq i kap)
      (while (> i 0)
        (setq kons (cons (cons i (+ vin (* *FQ-PITCH* (- kap i)))) kons)
              i    (1- i)))
      ;; jedan konektor odlazi na napajanje, izlazima ostaje kap-1
      (setq no (max 1 (min spl (1- kap))))
      (if (< no spl)
        (princ (fq:tf "kuc.spl.nestaje.kutija" (list (itoa spl) (itoa kap) (itoa no)))))
      (setq i no)
      (while (> i 0)
        (setq vizl (cons (+ vin (* *FQ-PITCH* (- kap i))) vizl)
              i    (1- i)))
      (setq k  no
            lw *FQ-ZOK-WS*))
    (progn
      ;; Konektor po vlaknu, broj se racuna unazad od kapaciteta.
      ;; Konektori koji jos nisu u upotrebi nastavljaju isti raster
      ;; ispod snopa, jer su i oni u kutiji.
      (setq n (max 1 (min k (length locs)))
            i 1)
      (while (<= i n)
        (setq kons (cons (cons (- kap (1- i)) (cadr (nth (1- i) locs))) kons)
              i    (1+ i)))
      ;; min, ne poslednji dodat: kad kuciste opsluzuje dva kabla,
      ;; spisak vlakana ide po rednom mestu konektora, pa poslednji u
      ;; listi ne mora da bude i najnizi
      (setq v  (apply 'min (mapcar 'cdr kons))
            pn (- kap n))
      (while (> pn 0)
        (setq v    (- v *FQ-PITCH*)
              kons (cons (cons pn v) kons)
              pn   (1- pn)))
      (setq kons (reverse kons)
            lw   *FQ-ZOK-W*)))

  (setq vsvi (mapcar 'cdr kons)
        vtop (apply 'max vsvi)
        vbot (apply 'min vsvi)
        ur   (+ u0 *FQ-ZOK-DX*)
        ul   (- ur lw)
        vt   (+ vtop *FQ-ZOK-MARG*)
        vb   (- vbot *FQ-ZOK-MARG*)
        cu   (* 0.5 (+ ul ur)))

  (fq:zokram oz adr mod kap k kon mnt spl st ul ur vt vb cu)

  (foreach p kons
    (fq:kon u0 (cdr p) *FQ-ZOK-KS* "FQ_ODF")
    (fq:Wtext (- u0 *FQ-ZOK-NUMX*) (cdr p) (* 0.42 *FQ-ZOK-TITH*)
              (itoa (car p)) 7 "FQ_ZOK" "FQ_TXT" 2 2))

  (if spl
    (progn
      (setq ozs (strcat oz "-SP"))
      (fq:zokspl ozs spl st u0 vin vizl)
      ;; napojno vlakno zavrsava na poslednjem konektoru
      (if (setq rec (car fl))
        (fq:addveza (caddr rec) (strcat oz "/" (itoa kap)))))
    (progn
      (setq i 1)
      (while (<= i (min k (length fl)))
        (setq rec (nth (1- i) fl)
              pn  (- kap (1- i)))
        (if rec (fq:addveza (caddr rec) (strcat oz "/" (itoa pn))))
        (setq i (1+ i)))))

  ;; Montaza kroz fq:kodnaz, isto kao u ramu i kao u fq:drawodo -- vidi
  ;; tamo zasto: ista vrednost ide na tri mesta i sva tri prate jezik.
  ;; Model i montaza kroz fq:bomd, ne kao go razmak u samoj niski:
  ;; katalog se isporucuje BEZ modela, pa je prazan model pravilo a ne
  ;; izuzetak i "ZOK 1    zid" je videla svaka prva upotreba.  Montaza
  ;; sme da bude prazna kod vrste koju je korisnik sam dodao i kod
  ;; starog zapisa koji je fq:kucprepis procitao bez tog polja.
  (princ (fq:tf "zok.nacrtan" (list oz (fq:bomd "  " mod)
                                    (fq:bomd "  " (fq:kodnaz "mnt" mnt))
                                    (itoa kap) kon (if spl (fq:tf "zok.spliter" (list (itoa spl) (itoa kap) (itoa no))) (fq:tf "zok.iskorisceno" (list (itoa k) (itoa kap)))) (fq:stnaziv st)))))


;;; ------------------------------------------------------------------
;;;  3b. ODF I PATCH PANEL, CRTANJE
;;;
;;;  fq:drawodf iz fq-sema.lsp ostaje netaknut, njime i dalje radi
;;;  stara komanda FQODF.  Ovaj crtac se od njega razlikuje u dvoje:
;;;
;;;   1. crta SVE konektore, i one na kojima jos nista ne stoji, jer
;;;      letva fizicki ima sve adaptere;
;;;   2. jedan razdelnik moze da nosi vise modula.  L1 sa dva modula od
;;;      96 se crta kao dva ista rama jedan ispod drugog, poravnata sa
;;;      kablom, sa oznakama "L1, M1" i "L1, M2".  Numeracija krece od
;;;      1 u svakom modulu, a modul stoji u adresi vlakna, pa se vlakno
;;;      cita kao "OR 1 L1, M2/45".
;;;
;;;  Oznaka oz vec nosi prefiks svoje vrste, pa se *FQ-ODF-PRE* ne
;;;  koristi i ne moze da se udvoji.
;;; ------------------------------------------------------------------

;; "L1, M1" i modul 2 daju "L1, M2".  Broj modula se trazi na kraju
;; oznake, iza slova M; ako ga nema, modul se dopisuje.
(defun fq:lmmod (lm m / i c d)
  (setq i (strlen lm) d "")
  (while (and (> i 0)
              (setq c (substr lm i 1))
              (>= c "0") (<= c "9"))
    (setq d (strcat c d)
          i (1- i)))
  (cond
    ((and (/= d "") (> i 0) (= (strcase (substr lm i 1)) "M"))
       (strcat (substr lm 1 i) (itoa (+ (atoi d) m -1))))
    ((= m 1) lm)
    (T (strcat lm ", M" (itoa m)))))
;; Oznake svih nm modula jednog razdelnika, redom odozgo nanize.
;;
;; Ako oznaka ne nosi broj modula ("L1"), a modula ima vise, dobio bi
;; prvi modul "L1" a drugi "L1, M2".  Zato se prvom dopisuje M1, pa
;; fq:lmmod dalje broji od njega.
;;
;; Prazna oznaka je svoj slucaj: kod jednog modula ostaje prazna i
;; natpisa nema, ali kod vise modula MORA da se razlikuju -- veza
;; vlakna se cita bas po toj niski (fq:kucvez), pa bi dva modula bila
;; jedno te isto.  Tada postaje "M1", "M2", bez izmisljene letve.
;;
;; Racun stoji ovde, a ne u fq:drawodfn, zato sto sirina rama zavisi od
;; NAJDUZE oznake modula (fq:odfsir): pregled mora da je zna pre nego
;; sto je ijedan modul nacrtan.  lm i nm koje pregled ima u ruci su bas
;; oni koje ce crtac dobiti -- fq:kucpost ih prosledjuje kroz fq:kucdraw
;; nepromenjene -- pa obe strane racunaju iz istog ulaza.
;;
;; nm se svodi na najmanje 1 i ovde, jer je to do sada radio fq:drawodfn
;; a sada je prvi pozivalac pregled.
(defun fq:odflms (lm nm / m out)
  (setq nm (max 1 (if nm nm 1)))
  (cond
    ((= lm "") (if (> nm 1) (setq lm "M1")))
    ((and (> nm 1) (= (fq:lmmod lm 2) (strcat lm ", M2")))
       (setq lm (strcat lm ", M1"))))
  (setq m nm out nil)
  (while (> m 0)
    (setq out (cons (fq:lmmod lm m) out)
          m   (1- m)))
  out)

;; Visine svih n konektora, redom odozgo nanize.  Gde postoji stvarno
;; vlakno uzima se njegova visina, da konektor i vlakno legnu u isti
;; red; ispod poslednjeg vlakna nastavlja se isti raster.
;; Raster se nastavlja od NAJNIZE dosad izracunate visine, ne od
;; poslednje: kad kuciste opsluzuje dva kabla, spisak vlakana ide po
;; rednom mestu konektora, pa poslednji u listi ne mora da bude najnizi.
(defun fq:odfvis (locs n / out i v)
  (setq out nil i 1 v nil)
  (while (<= i n)
    (setq v   (if (<= i (length locs))
                (cadr (nth (1- i) locs))
                (- (apply 'min out) *FQ-PITCH*))
          out (cons v out)
          i   (1+ i)))
  (reverse out))

;; Sirina rama razdelnika vise ne stoji ovde: fq:odfsir i fq:odfw su u
;; fq-sema.lsp, pored *FQ-ODF-DESNO* i *FQ-ODF-PAD* od kojih zavise i
;; pored fq:drawodf, koji je istu meru racunao u sebi.  Dve kopije istog
;; racuna su i dovele do toga da fq:pickbox dobije samo desnu kolonu
;; rama umesto pune sirine.  Ovde ostaje samo trag, da se ne trazi.

;; Jedan modul: ram, pregrada, natpisi i svih kap konektora.
;;   hs      visine kap konektora ovog modula, odozgo nanize
;;   fl      vlakna poravnata sa hs, sme da bude krace ili prazno
;;   km      koliko je konektora ovog modula iskorisceno
;;   naslov  da li iznad rama idu oznaka i model; kod drugog i daljeg
;;           modula ne idu, jer pripadaju istom razdelniku
(defun fq:odfmod (tip oz mod lmm kap km kon st u0 ul ud ur vt vb hs fl
                  naslov / cu j v rec)
  (setq cu (* 0.5 (+ ul ur)))
  ;; status ostaje POSLEDNJE polje, fq:vstat ga tako cita
  (fq:xd (fq:Wrect ul vt ur vb 7 "FQ_ODF")
         (list tip oz mod (itoa kap) (itoa km) kon lmm st))
  (fq:Wline ud vt ud vb 7 "FQ_ODF" nil)
  (if naslov
    (progn
      ;; Prefiks vrste ide SAMO u naslov crteza, nikad u oz.  Kucista
      ;; nacrtana starom komandom FQODF nose golu oznaku i u xdata i u
      ;; vezama vlakana ("NAZIV L1, M1/3"); kad bi se oz prefiksirao,
      ;; fq:addveza ne bi prepoznao duplikat pa bi svako vec povezano
      ;; vlakno dobilo drugi zavrsetak i FQBUDZET bi brojao duplo.
      ;; fq:kucoz je idempotentan, pa nove oznake ostaju nepromenjene.
      (fq:Wtext cu (+ vt *FQ-ODF-TIT1*) *FQ-ODF-TITH*
                (if (= oz "") oz (fq:kucoz tip oz))
                7 "FQ_ODF" "FQ_TXTB" 1 2)
      ;; Prazan model se ne crta uopste.  entmake bi i za "" napravio
      ;; TEXT entitet: nevidljiv, ali ga ssget hvata, GRIPS pokazuje i
      ;; specifikacija broji kao natpis iznad rama.
      (if (/= mod "")
        (fq:Wtext cu (+ vt *FQ-ODF-TIT2*) *FQ-ODF-TITH*
                  mod 7 "FQ_ODF" "FQ_TXTB" 1 2))))
  ;; Prazna oznaka letve se ne crta uopste -- isto pravilo i isti razlog
  ;; kao kod praznog modela dva reda iznad: fq:Wtext bi i za "" napravio
  ;; TEXT entitet koji se ne vidi ali ga ssget hvata.
  (if (/= lmm "")
    (fq:Wtext (* 0.5 (+ ul ud)) (* 0.5 (+ vt vb)) *FQ-ODF-TITH*
              lmm 7 "FQ_ODF" "FQ_TXTB" 1 2))
  (setq j 1)
  (while (<= j kap)
    (setq v (nth (1- j) hs))
    (fq:kon u0 v *FQ-ODF-KS* "FQ_ODF")
    (fq:Wtext (- u0 *FQ-ODF-NUMX*) v *FQ-ODF-NUMH*
              (itoa j) 7 "FQ_ODF" "FQ_TXT" 2 2)
    (if (and (<= j km) (setq rec (nth (1- j) fl)))
      (fq:addveza (caddr rec) (strcat (fq:spoj oz " " lmm) "/" (itoa j))))
    (setq j (1+ j)))
  T)

;; k je ukupan broj iskoriscenih konektora, kroz sve module redom.
;;
;; Moduli se dodiruju ivicama: granica izmedju dva modula ide na pola
;; rastera, inace bi se ramovi preklopili za dve margine.  Kod jednog
;; modula sve ostaje kao u fq:drawodf, margina *FQ-ODF-MARG*.
(defun fq:drawodfn (tip oz mod lm kap nm k kon st fl ang
                    / locs u0 hs lms w ur ud ul m idx km vt vb hm)
  (fq:init)
  (fq:sistem ang (fq:oglsad fl ang))
  (setq locs   (mapcar '(lambda (r) (fq:loc (cadr r))) fl)
        u0     (car (car locs))
        nm     (max 1 nm)
        hs     (fq:odfvis locs (* kap nm)))
  ;; Oznake modula dolaze iz fq:odflms, koju je pregled (fq:kucpost)
  ;; vec pozvao sa ISTIM lm i nm da bi dobio sirinu rama -- pravilo
  ;; parnosti.  Racun je otisao tamo jer sirina zavisi od najduze
  ;; oznake, a pregled crta siluetu pre nego sto modula uopste ima.
  ;;
  ;; lm se vraca na oznaku PRVOG modula: zavrsna poruka o nacrtanom
  ;; razdelniku prijavljuje nju, a to je tacno ono sto je stara grana
  ;; ostavljala u lm ("L1" -> "L1, M1", "" -> "M1" kad ima vise modula).
  (setq lms (fq:odflms lm nm)
        lm  (car lms))
  ;;  Puna sirina rama kroz fq:odfw, ista funkcija koju je pregled
  ;;  predao fq:pickbox-u, i ul se meri od DESNE ivice kao u fq:boxvecs
  ;;  (ul = dx - w).  Preko ud je izlazio isti broj, ali druga formula --
  ;;  i bas ta razlika je sakrila da pregled dobija samo desnu kolonu.
  ;;  ud ostaje, po njemu ide pregrada i sredina natpisa letve.
  (setq w  (fq:odfw lms)
        ur (+ u0 *FQ-ODF-DX*)
        ud (- ur *FQ-ODF-DESNO*)
        ul (- ur w)
        m  1)

  (while (<= m nm)
    (setq idx (* (1- m) kap)
          hm  (fq:prvih (fq:podlista hs (1+ idx)) kap)
          km  (max 0 (min kap (- k idx)))
          ;; max i min, ne prvi i poslednji: kad kuciste opsluzuje dva
          ;; kabla, spisak vlakana ide po rednom mestu konektora, pa
          ;; najvisi konektor ne mora da bude prvi u listi
          vt  (if (= m 1)
                (+ (apply 'max hm) *FQ-ODF-MARG*)
                (+ (apply 'max hm) (* 0.5 *FQ-PITCH*)))
          ;;  Poslednji modul ide do dna SNOPA, ne do poslednjeg
          ;;  konektora.
          ;;
          ;;  Letva ima tacno kap adaptera i vise ih ne moze dobiti, ali
          ;;  na terenu se u patch panel od 24 redovno uvodi kabl od 48:
          ;;  24 vlakna idu na konektore, ostala ostaju u kaseti.  Do
          ;;  23.08.2026 je ram stajao na 24 reda pa je pola snopa
          ;;  visilo ISPOD kucista, kao da u njega ne ulazi -- a
          ;;  fq:kucspoji je uz svaki kabl uzalud precrtavao isti taj
          ;;  ram, jer se posle precrtavanja nista nije menjalo.
          ;;
          ;;  Ram se zato spusta do najnizeg vlakna, isto kao kod ODO
          ;;  ormara.  Konektora i dalje ima kap i numeracija se ne
          ;;  dira; visak vlakana je rezerva u kaseti i tako se i vidi.
          vb  (if (= m nm)
                (- (min (apply 'min hm) (apply 'min (mapcar 'cadr locs)))
                   *FQ-ODF-MARG*)
                (- (apply 'min hm) (* 0.5 *FQ-PITCH*))))
    (fq:odfmod tip oz mod (nth (1- m) lms) kap km kon st
               u0 ul ud ur vt vb hm (fq:podlista fl (1+ idx)) (= m 1))
    (setq m (1+ m)))

  ;; Model kroz fq:bomd, iz istog razloga kao u fq:drawzok: katalog ga
  ;; ne isporucuje.  Letva ne treba -- i fq:odfask i fq:kucask praznu
  ;; vracaju na podrazumevanu, pa do ovde ne stize prazna.
  (princ (fq:tf "pp.nacrtan" (list oz (fq:bomd "  " mod) (fq:bomd "  " lm) (if (> nm 1) (fq:tf "pp.moduli" (list (itoa nm) (itoa kap))) (fq:tf "pp.jedan" (list (itoa kap)))) (itoa k) (itoa (* kap nm)) (fq:stnaziv st)))))


;;; ------------------------------------------------------------------
;;;  3c. ODO I MDU SA SPLITEROM U ORMARU
;;;
;;;  Isto sto ZOK vec ima, samo u ormaru: u kuciste ulazi vlakno, ono
;;;  zavrsava na portu, sa tog porta ide patch u telo splitera, a
;;;  lepeza izlazi na sledece portove.  Telo dobija svoj xdata zapis
;;;  "SPLITER", pa ga specifikacija broji kao zasebnu stavku i opticki
;;;  budzet vidi njegovo slabljenje.
;;;
;;;  IZLAZI IDU IZNAD SNOPA, I NOSE BROJEVE 1..n.
;;;
;;;      ODO 48, kabl donosi jedno vlakno, spliter 1:8
;;;
;;;        +-------------+  1  \
;;;        |       +---+ |  2   |
;;;        |  1:8  |   | | ...  |  izlazi splitera
;;;        |       +---+ |  8  /
;;;        +-------------+ 48  <- dolazno vlakno, odavde patch u telo
;;;
;;;  Do 23.08.2026 je bilo obrnuto: dolazno vlakno na vrhu, izlazi
;;;  NANIZE ispod njega, numerisani unazad od kapaciteta (47, 46, ...).
;;;  Racunski je to bilo urednije -- raster se prosto nastavljao -- ali
;;;  je u praksi kvarilo bas ono zbog cega se ormar i polaze unapred:
;;;  kada se posle doda privodni kabl, njegovo prvo vlakno legne na
;;;  napojni port, a vlakna 2..12 padnu tacno na redove na kojima vec
;;;  stoje izlazi splitera.  Dva vlakna na istom portu, i to bez ijedne
;;;  poruke.
;;;
;;;  Zato napojni port ide DOLE, a izlazi iznad njega: ispod napojnog
;;;  reda je onda prazno i ceo kabl ima kuda.  Brojevi izlaza krecu od 1
;;;  jer su to prvi portovi ormara odozgo; napojni zadrzava kapacitet
;;;  (ODO 48 -> port 48), sto je i inace prvi broj koji ormar deli.
;;;
;;;  Ram i dalje raste u jednom pravcu od pokazane tacke, pa pregled uz
;;;  mis radi -- samo NAVISE.  fq:boxvecs zato ram racuna iz najviseg i
;;;  najnizeg reda, a ne iz prvog i poslednjeg clana liste.
;;; ------------------------------------------------------------------

;;  Redovi izlaza: no redova IZNAD reda vtop, poredjani od izlaza 1
;;  (najvisi) do izlaza no (odmah iznad snopa).
;;
;;  Jedan racun za crtaca i za pregled uz mis, po pravilu parnosti:
;;  fq:drawodospl ga zove sa stvarnim vrhom snopa, fq:kucpost sa nulom.
(defun fq:izlgore (vtop no / i out)
  (setq i 1 out nil)
  (while (<= i no)
    (setq out (cons (+ vtop (* *FQ-PITCH* i)) out)
          i   (1+ i)))
  out)

;; Telo splitera u ormaru.
;;   u0     kolona portova
;;   vfeed  visina porta sa koga se spliter napaja
;;   vizl   visine portova na koje idu izlazi, redom 1..n
(defun fq:odospl (ozs spl st u0 vfeed vizl
                  / g kl su1 su2 sv1 sv2 vmid ux j dx)
  (setq g    (fq:splgub spl)
        kl   (- u0 *FQ-ODO-PW*)          ; leva ivica simbola porta
        vmid (/ (apply '+ vizl) (float (length vizl)))
        ;; Kod velikog odnosa lepeza je visoka po desetak jedinica, pa
        ;; bi se pri stalnom razmaku slila u trougao.  Telo se zato
        ;; blago odmakne, isto kao kod samostalnog splitera, ali ne
        ;; dalje nego da ulaz ostane desno od pregrade ormara.
        dx   (min (- *FQ-ODO-W* *FQ-ODO-LEV* *FQ-SPL-W* *FQ-SPL-VX* 1.0)
                  (max *FQ-ODO-SPD* (fq:spldx (length vizl))))
        su2  (- u0 dx)
        su1  (- su2 *FQ-SPL-W*)
        sv1  (+ vmid (* 0.5 *FQ-SPL-H*))
        sv2  (- vmid (* 0.5 *FQ-SPL-H*))
        ux   (- su1 *FQ-SPL-VX*))
  ;; ulaz: napojni port -> levo -> do visine tela -> u telo
  (fq:Wline kl vfeed ux vfeed 5 "FQ_SPLITER" nil)
  (fq:Wline ux vfeed ux vmid  5 "FQ_SPLITER" nil)
  (fq:Wline ux vmid  su1 vmid 5 "FQ_SPLITER" (list "ULAZ" ozs st))
  (fq:xd (fq:Wrect su1 sv1 su2 sv2 5 "FQ_SPLITER")
         (list "SPLITER" ozs (strcat "1:" (itoa spl)) (rtos g 2 1) st))
  (fq:Wtext (* 0.5 (+ su1 su2)) vmid (* 0.62 *FQ-SPL-TITH*)
            (strcat "1:" (itoa spl)) 5 "FQ_SPLITER" "FQ_TXTB" 1 2)
  (fq:Wtext su1 (- sv2 (* 0.55 *FQ-SPL-TITH*)) (* 0.55 *FQ-SPL-TITH*)
            (strcat (rtos g 2 1) " dB") 5 "FQ_SPLITER" "FQ_TXT" 0 0)
  ;; lepeza: telo -> leva ivica svakog izlaznog porta
  (setq j 1)
  (foreach v vizl
    (fq:Wline su2 vmid kl v 5 "FQ_SPLITER" (list "IZLAZ" ozs (itoa j) st))
    (setq j (1+ j))))

;; Ormar sa spliterom.
;;   k    koliko vlakana kabla stoji na portovima (bar jedno, napojno)
;;   fdi  redno mesto vlakna koje napaja spliter, unutar tih k
;;   fl   PUN spisak vlakana kabla; ram se racuna iz njega, kao i inace
;;
;;  Izlazi idu na portove IZNAD celog snopa, da ne padnu na red na kome
;;  vec lezi neko vlakno i da ispod napojnog reda ostane prazno za
;;  privodni kabl -- vidi obrazlozenje uz odeljak 3c.
(defun fq:drawodospl (oz adr kap tip mnt k spl fdi st fl ang
                      / locs u0 vtop vlast vizl no ur ul vt vb ud cu
                        i v pn rec nv ktot ozs vfeed)
  (fq:init)
  (fq:sistem ang (fq:oglsad fl ang))
  (setq locs   (mapcar '(lambda (r) (fq:loc (cadr r))) fl)
        u0     (car (car locs))
        vtop   (apply 'max (mapcar 'cadr locs))
        vlast  (apply 'min (mapcar 'cadr locs))
        ;; bar jedan port mora da ostane slobodan za izlaz, inace bi
        ;; numeracija izlaza otisla u nulu i minus
        k      (max 1 (min k (length locs) (1- kap)))
        fdi    (max 1 (min fdi k))
        no     (max 1 (min spl (- kap k)))
        vfeed  (cadr (nth (1- fdi) locs))
        ktot   (+ k no)
        ozs    (strcat oz "-SP"))
  (if (< no spl)
    (princ (fq:tf "kuc.spl.nestaje.ormar" (list (itoa spl) (itoa kap) (itoa k) (itoa no)))))
  ;; redovi izlaza, IZNAD celog snopa; izlaz 1 je najvisi
  (setq vizl (fq:izlgore vtop no))

  (setq ur (+ u0 *FQ-ODO-DX*)
        ul (- ur *FQ-ODO-W*)
        vt (+ (car vizl) *FQ-ODO-MARG*)
        vb (- vlast *FQ-ODO-MARG*)
        ud (+ ul *FQ-ODO-LEV*)
        cu (* 0.5 (+ ul ur)))
  ;; Zapis je isti kao kod obicnog ormara, polje po polje, da ga
  ;; fq:kucpolja, fq:tally i FQDODAJ citaju bez ijednog izuzetka.
  ;; Da spliter u ormaru postoji vidi se iz samog tela, fq:splukut.
  ;; Montaza kao KOD, isto kao u fq:drawodo -- ova dva zapisa moraju da
  ;; ostanu identicna polje po polje, inace fq:kucpolja i fq:tally
  ;; ormar sa spliterom citaju drugacije nego ormar bez njega.
  (fq:xd (fq:Wrect ul vt ur vb 7 "FQ_ODO")
         (list "ODO" oz (itoa kap) (itoa ktot) tip
               (fq:nazkod "mnt" mnt) adr st))
  (fq:Wline ud vt ud vb 7 "FQ_ODO" nil)
  (fq:Wtext cu (+ vt *FQ-ODO-TIT1*) (* 2.2 *FQ-TXTH*)
            (strcat oz " (" (itoa kap) ")") 7 "FQ_ODO" "FQ_TXTB" 1 2)
  ;; Prazna adresa se ne crta -- vidi istu granu u fq:drawodo.
  (if (/= adr "")
    (fq:Wtext cu (+ vt *FQ-ODO-TIT2*) (* 2.2 *FQ-TXTH*)
              adr 7 "FQ_ODO" "FQ_TXTB" 1 2))
  ;; tip je model ormara i sme da bude prazan -- katalog se isporucuje
  ;; bez ijednog modela.  Ista grana, doslovno, stoji u fq-sema.lsp uz
  ;; fq:drawodo: dve funkcije crtaju isti natpis na istom mestu istog
  ;; rama, pa moraju i da izgledaju isto kad se uporede.
  (fq:Wtext (* 0.5 (+ ul ud)) (+ vb 1.15) (* 0.70 *FQ-TXTH*)
            (if (= tip "") (itoa kap) (strcat tip "-" (itoa kap)))
            7 "FQ_ODO" "FQ_TXT" 1 2)
  (fq:Wtext (* 0.5 (+ ul ud)) (+ vb 0.55) (* 0.70 *FQ-TXTH*)
            (fq:kodnaz "mnt" mnt) 7 "FQ_ODO" "FQ_TXT" 1 2)

  ;; portovi vlakana, numerisani unazad od kapaciteta
  (setq i 1)
  (while (<= i k)
    (setq rec (nth (1- i) fl)
          v   (cadr (nth (1- i) locs))
          pn  (- kap (1- i)))
    (fq:port u0 v pn)
    (if rec (fq:addveza (caddr rec) (strcat oz "/" (itoa pn))))
    (setq i (1+ i)))

  ;;  Portovi izlaza, brojevi 1..no odozgo nanize.
  ;;
  ;;  Ne nastavljaju numeraciju kabla (47, 46, ...) nego krecu od jedan:
  ;;  to su prvi portovi ormara odozgo, a izlaz splitera je i u praksi
  ;;  poznat po svom rednom broju u lepezi.  Vlakno lepeze i port tako
  ;;  nose isti broj -- kod odnosa 1:16 idu 1 do 16, bez racuna u glavi.
  (setq i 1)
  (foreach v vizl
    (fq:port u0 v i)
    (setq i (1+ i)))

  (fq:odospl ozs spl st u0 vfeed vizl)

  ;; ostatak snopa visi u ormaru, isto kao kod obicnog ormara
  (setq nv 0)
  (if *FQ-ODO-VISI*
    (progn
      (setq i (1+ k))
      (while (<= i (length locs))
        (if (not (fq:prolaziu (caddr (nth (1- i) fl)) ul u0 vt vb))
          (progn (fq:visi u0 (cadr (nth (1- i) locs)))
                 (setq nv (1+ nv))))
        (setq i (1+ i)))))

  ;; Ceo segment ispada kad modela nema -- vidi istu poruku uz fq:drawodo
  ;; u fq-sema.lsp: golo "12" iza "(12)" je kapacitet dvaput.
  (princ (fq:tf "odospl.nacrtan" (list oz (itoa kap) (fq:bomd " " (fq:spoj (if (= tip "") "" (strcat tip "-" (itoa kap))) " " (fq:kodnaz "mnt" mnt))) (itoa spl) (itoa (- kap (1- fdi))) "1" (itoa no) (fq:stnaziv st) (if (> nv 0) (fq:tf "odo.visi" (list (itoa nv))) "")))))


;;; ------------------------------------------------------------------
;;;  4. ZAJEDNICKO CRTANJE
;;;
;;;  Jedna ulazna tacka za sve vrste kucista.  Grana po crtacu iz
;;;  kataloga, pa ODF i Patch panel dele fq:drawodfn, a ODO i MDU
;;;  dele fq:drawodo.  Razlika je u oznaci, modelu i xdata zapisu.
;;; ------------------------------------------------------------------

(defun fq:kucdraw (tip oz mod kap nm k kon mnt lm adr spl st fl ang / crt mk)
  (setq crt (fq:kc-crt tip))
  (cond
    ((= crt "odf")
       (fq:drawodfn tip oz mod lm kap nm k kon st fl ang))
    ((= crt "odo")
       ;; marker pre crtanja: fq:kucprepis posle ide entnext od njega
       (setq mk (entlast))
       ;; Ormar sa spliterom prima JEDNO dolazno vlakno, isto kao ZOK
       ;; kutija: ono zavrsava na poslednjem portu i odatle napaja
       ;; spliter.  Ormar sa punim kablom PA spliterom pravi se obrnutim
       ;; redom, komandom FQSPLKUC nad vec nacrtanim ormarom.
       (if spl
         (fq:drawodospl oz adr kap mod mnt 1 spl 1 st fl ang)
         (fq:drawodo oz adr kap mod mnt k st fl ang))
       (if (= tip "IPAN") (fq:kucprepis mk "ODO" "IPAN")))
    ((= crt "zok")
       ;; adr se do 24.08.2026 ovde TIHO GUBILA: dijalog polje ostavlja
       ;; upaljeno za kutiju (mode_tile "adr" gasi ga samo za odf/pp), a
       ;; fq:kucdash je izricito pita -- pa je korisnik unosio adresu koja
       ;; nigde nije stizala.  Nije bilo "polja nema" nego "pita se i baca".
       (fq:drawzok oz adr mod kap k kon mnt spl st fl ang))
    (T (princ (fq:tf "kuc.nepoznat.crtac" (list tip))))))

;; Menja vrstu u xdata zapisu rama koji je upravo nacrtan.
;;
;; AutoLISP NEMA kretanje unazad kroz bazu: postoje entnext, entlast i
;; entget, ali entprev ne postoji.  Zato se pre crtanja zapamti tada
;; poslednji entitet, a posle se ide entnext od njega do kraja crteza.
;; Tako se gleda tacno ono sto je ovo crtanje napravilo, bez obzira
;; koliko je to entiteta.
;;
;; marker sme da bude nil, sto znaci prazan crtez pre crtanja; tada se
;; krece od prvog entiteta.
(defun fq:kucprepis (marker stari novi / e d n)
  (setq e (if marker (entnext marker) (entnext))
        n 0)
  (while e
    (setq d (fq:xdget e))
    (if (and d (= (car d) stari))
      (progn (fq:xd e (cons novi (cdr d)))
             (setq n (1+ n))))
    (setq e (entnext e)))
  (if (= n 0)
    (princ (fq:tf "kuc.nema.rama" (list stari novi))))
  n)


;;; ------------------------------------------------------------------
;;;  5. POSTAVLJANJE
;;;
;;;  Isti obrazac koji vec koriste fq:odfask i fq:odoask: kuciste se
;;;  kaci na kraj kabla, ili stoji samostalno pa se posle njega kabl
;;;  poravnava sam.
;;; ------------------------------------------------------------------

;; Ima li u crtezu ijedno FiberQ vlakno na koje bi kuciste moglo da se
;; zakaci.  Bez ove provere prvi element u praznom crtezu ne bi mogao
;; da se postavi, jer bi kuciste trazilo kabl koji jos ne postoji.
(defun fq:imavlakana ( / ss)
  (setq ss (fq:ssx '((8 . "FQ_VLAKNO") (-3 ("FIBERQ")))))
  (and ss (> (sslength ss) 0)))

;; Koliko vlakana kabla ide na konektore.
;;
;; pod je ono sto bi se desilo i bez pitanja, pa Enter zadrzava staro
;; ponasanje.  Nula je dozvoljena i znaci da nijedno vlakno ne ide na
;; konektor: sav snop ostaje u kucistu i spaja se kasnije, komandom
;; FQDODAJ.
(defun fq:pitajvl (pod naj / n)
  (if (not *FQ-PITAJ-VL*)
    pod
    (progn
      (if (not (setq n (getint (fq:tf "kuc.pitaj.nakonektore" (list (itoa pod) (itoa naj))))))
        (setq n pod))
      (max 0 (min n naj)))))

;; Raspored virtuelnih vlakana za samostalno postavljanje, BEZ pitanja.
;; fq:strukt iz fq-sema.lsp pita "Vlakana po cevcici" i "Broj cevcica",
;; a to nema smisla kad je kapacitet vec izabran dugmetom: ODF 144 zna
;; da mu treba 12x12.  Racuna se ista vrednost koju fq:strukt nudi kao
;; podrazumevanu, samo se ne pita.
(defun fq:strukt0 (k / nf nt)
  (setq nf (if (< k 12) k 12))
  (if (< nf 1) (setq nf 1))
  (setq nt (max 1 (fix (/ (+ k nf -1) nf))))
  (list nt nf))

;; Oznaka uvek nosi prefiks svoje vrste.  Prazno polje znaci sledeci
;; slobodan broj, a ako je korisnik prefiks vec otkucao, ne dodaje se
;; drugi put.
;; s se trimuje: polje dijaloga vraca tacno ono sto je ukucano, pa je
;; oznaka od samog razmaka prolazila kroz (= s "") kao da nije prazna i
;; davala "ODO  " umesto sledeceg slobodnog broja.  Takva oznaka posle
;; nigde ne moze da se nadje -- fq:posle trazi cifre iza prefiksa.
(defun fq:kucoz (tip s / pre)
  (setq pre (fq:vrpre tip)
        s   (fq:trim s))
  (cond ((= s "") (fq:next pre))
        ((and (>= (strlen s) (strlen pre))
              (= (strcase (substr s 1 (strlen pre))) (strcase pre))) s)
        (T (strcat pre s))))

;; k sme da bude nil: tada se uzima onoliko konektora koliko kabl ima
;; vlakana, a u praznom crtezu razumna podrazumevana vrednost.
;; nm je broj modula, ima smisla samo kod ODF-a i patch panela.
(defun fq:kucpost (tip oz mod kap nm k kon mnt lm adr spl st sam
                   / pc fl ang n nn rows p0 lc u0 v0 sirina kw odustao tot
                     odospl zokspl npr)
  (fq:init)
  (setq odustao nil
        nm      (max 1 (if nm nm 1))
        tot     (* kap nm))

  ;; prazan crtez: nema na sta da se kaci, ide samostalno bez pitanja
  (if (and (not sam) (not (fq:imavlakana)))
    (progn
      (princ (fq:t "kuc.nema.kabla"))
      (setq sam T)))

  ;; kacenje na kabl, sa izlazom na samostalno ako se odustane
  (if (not sam)
    (progn
      (setq pc (fq:pickcable (fq:tf "kuc.pitaj.vlakno" (list (fq:vrime tip)))))
      (if (not pc)
        (progn
          (if (fq:dvatok "opste.tok.da" "opste.tok.ne"
                         "kuc.pitaj.samostalno" nil)
            (setq odustao T)
            (setq sam T))))))

  (cond
    (odustao (princ (fq:t "opste.otkazano")))

    ;; --- kaci se na kabl -----------------------------------------
    ((not sam)
       (setq fl  (cadr pc)
             ang (caddr pc)
             n   (length fl))
       ;; k koje niko nije zadao dolazi sa dugmeta na traci.  Tada se
       ;; broj vlakana pita u komandnoj liniji, jer se tek sada zna
       ;; koliko ih kabl stvarno donosi.
       (if (and (not k) (not spl))
         (setq k (fq:pitajvl (min n tot) (min n tot))))
       (if (not k) (setq k n))
       ;; kod kutije sa spliterom broj izlaza odredjuje spliter,
       ;; a ne koliko vlakana kabl slucajno ima
       (if spl (setq k spl) (setq k (min k n)))
       (setq k (max 0 (min k tot)))
       (fq:kucdraw tip oz mod kap nm k kon mnt lm adr spl st fl ang))

    ;; --- stoji samostalno ----------------------------------------
    (T
       ;; Bez kabla nema odakle da se zna koliko je konektora u upotrebi,
       ;; pa se zadrzavaju podrazumevane vrednosti koje su stare komande
       ;; vec imale: ODF i patch panel crtaju pun kapacitet, ormari samo
       ;; prvih nekoliko portova.  ZOK je kutija sa fizickim konektorima,
       ;; pa ide pun kapacitet.
       (if (not k)
         (setq k (if (= (fq:kc-crt tip) "odo") (min 6 kap) tot)))
       (setq k      (max 1 (min k tot))
             ang    *fq-ugao*
             ;;  Oba kucista sa spliterom rastu IZNAD dolaznog vlakna:
             ;;  kutija otkad postoji, ormar od 23.08.2026.  Razlika je
             ;;  samo u tome koliko redova pregled pokazuje -- kutija
             ;;  ima SVE konektore fizicki prisutne, ormar samo napojni
             ;;  i onoliko izlaza koliko ih spliter ima.
             odospl (and spl (= (fq:kc-crt tip) "odo"))
             zokspl (and spl (= (fq:kc-crt tip) "zok"))
             ;; koliko redova pregled treba da pokaze
             ;;  Razdelnik: crtac ram racuna po SVIH kap*nm redova --
             ;;  fq:odfvis produzava raster ispod poslednjeg vlakna do
             ;;  punog kapaciteta -- pa i pregled mora toliko.  Sa
             ;;  golim k je silueta bila kratka za (tot - k) rastera:
             ;;  -FQPP samostalno uzima k = 6, pa je za PP 24 silueta
             ;;  bila visoka 8.0 a nacrtan ram 21.6.
             npr    (cond (odospl (1+ (max 1 (min spl (1- kap)))))
                          (zokspl kap)
                          ((= (fq:kc-crt tip) "odf") tot)
                          (T      k))
             nn     (if spl (list 1 npr) (fq:strukt0 k))
             ;; Kuciste sa spliterom raste NAVISE: dolazno vlakno ostaje
             ;; tacno na pokazanoj tacki, a redovi idu iznad nje.  Nula
             ;; je zato POSLEDNJI clan liste, a ne prvi -- vidi
             ;; fq:boxvecs, koji ram racuna iz najviseg i najnizeg reda.
             ;;  Redovi razdelnika idu kroz fq:odfvis, isti racun koji
             ;;  crtac primenjuje: prvih k po rasteru cevcica (dakle sa
             ;;  *FQ-GAP* na prelazima), ostatak golim korakom nanize.
             ;;  Prepisan racun bi se razisao tiho, a razlika bi bila
             ;;  vidljiva tek kao odskok siluete.
             rows   (cond (spl (append (fq:izlgore 0.0 (1- npr)) (list 0.0)))
                          ((= (fq:kc-crt tip) "odf")
                             (fq:odfvis
                               (mapcar '(lambda (v) (list 0.0 v))
                                       (fq:redovi (car nn) (cadr nn) 0.0 nil))
                               tot))
                          (T (fq:redovi (car nn) (cadr nn) 0.0 nil)))
             sirina (cond ((= (fq:kc-crt tip) "odo") *FQ-ODO-W*)
                          ((= (fq:kc-crt tip) "zok")
                             (if spl *FQ-ZOK-WS* *FQ-ZOK-W*))
                          ;;  Razdelnik: PUNA sirina rama, po istim
                          ;;  oznakama modula koje ce dobiti i crtac --
                          ;;  lm i nm odavde idu pravo u fq:drawodfn,
                          ;;  kroz fq:kucdraw, nepromenjeni.  Golo
                          ;;  *FQ-ODF-DESNO* je pokazivalo samo desnu
                          ;;  kolonu, pa je silueta bila uza za celu
                          ;;  levu pregradu (5.80 naspram 11.60 do 16) i
                          ;;  kuciste je lako palo preko levog suseda.
                          (T (fq:odfw (fq:odflms lm nm))))
             ;;  Pregled ide za SVE, i za kutiju sa spliterom.
             ;;
             ;;  Do 23.08.2026 je ZOK sa spliterom isao golim fq:gp, jer
             ;;  je fq:redovi davao redove nanize pa bi pregled pokazao
             ;;  kutiju od jednog reda umesto od kap redova.  To vise ne
             ;;  vazi: redovi se grade fq:izlgore, navise, a fq:boxvecs
             ;;  ram racuna iz najviseg i najnizeg reda.  Zbog toga je
             ;;  FAT 12/16/24 -- jedini kojima katalog daje spliter --
             ;;  bio bez pregleda, dok su FAT 1..8 imali.
             p0     (fq:pickbox
                      (if spl
                        (fq:tf "kuc.pitaj.dolazno" (list (fq:vrime tip)))
                        (fq:tf "kuc.pitaj.prvo"    (list (fq:vrime tip))))
                      (fq:prvih rows npr)
                      ;; pomeraj kolone od desne ivice je po vrsti:
                      ;; ranije je za sve isao ODF-ov, pa je pregled
                      ;; ormara stajao pola jedinice u stranu
                      ;; margina po vrsti, iz istog razloga: ODO 2.00,
                      ;; razdelnik 1.60, kutija 1.50 -- do 23.08.2026 je
                      ;; fq:boxvecs za sve tri drzao zakucano 2.0
                      sirina (fq:kucdx (fq:kc-crt tip)) ang
                      (fq:kucmarg (fq:kc-crt tip))))
       (if (not p0)
         (princ (fq:t "opste.otkazano"))
         (progn
           ;; Samostalno postavljeno kuciste nikad nije ogledalno: kabla
           ;; jos nema, pa nema ni strane sa koje dolazi.  fq:sistem zato
           ;; izricito nosi nil, umesto da se oslanja na to sto je
           ;; fq:init ocistio *fq-m*.
           (fq:sistem ang nil)
           (setq lc     (fq:loc p0)
                 u0     (car lc)
                 v0     (cadr lc)
                 fl     (fq:virtfl u0 v0 (car nn) (cadr nn)
                                   (if spl 1 k) ang)
                 rows   (fq:redovi (car nn) (cadr nn) v0 nil))
           (fq:kucdraw tip oz mod kap nm k kon mnt lm adr spl st fl ang)
           ;;  Kod kucista sa spliterom se redovi NE namecu kablu.
           ;;
           ;;  Do 23.08.2026 su se kod ormara nametali, i tu je bio kvar
           ;;  koji se video tek na crtezu: lista je bila napojni red pa
           ;;  redovi izlaza, pa je privodni kabl svoje prvo vlakno
           ;;  spustao na napojni port, a vlakna 2..12 tacno na portove
           ;;  lepeze.  Sada je ispod napojnog reda prazno i kabl sme da
           ;;  racuna svoj raster sam -- a to je i jedini tacan raster,
           ;;  jer nosi razmak izmedju cevcica.
           ;;
           ;;  Poravnanje sada dobija SVAKO samostalno kuciste; ranije je
           ;;  ZOK sa spliterom ispadao, jer za njega ni tacka nije isla
           ;;  kroz pregled.
           (setq *fq-por* (list v0 ang (if spl nil rows) u0))
           (princ (fq:tf "kuc.poravnanje" (list oz (if spl (fq:t "kuc.por.vlakno") (fq:t "kuc.por.konektor")))))))))
  (princ))


;;; ------------------------------------------------------------------
;;;  6. DIJALOG
;;;
;;;  DCL ne ume da sakrije polje, ali ume da ga zasivi, pa se polja
;;;  koja tekuca vrsta nema iskljucuju sa mode_tile.
;;; ------------------------------------------------------------------

(defun fq:dclkuc ( / fn f i)
  (setq fn (strcat (getvar "TEMPPREFIX") "fq-kuc.dcl"))
  (setq f (open fn "w"))
  (if f
    (progn
      (fq:dcll f "fqkuc : dialog { label = \"" (fq:t "dcl.naslov.kuc") "\";")
      (write-line " : row {" f)
      (fq:dcll f "  : boxed_column { label = \"" (fq:t "dcl.kuc.vrsta") "\";")
      (write-line "   : radio_column {" f)
      (setq i 0)
      ;; Naziv vrste kroz fq:vrime: isporucena vrsta dobija naziv iz
      ;; jezicke tabele, korisnicka i ona kojoj je korisnik promenio
      ;; naziv prolaze iz kataloga nepromenjene -- vidi fq:vrime.
      ;; Kroz fq:dcll zbog navodnika: vrsta koju korisnik nazove
      ;; `rack 19"` je do bloka 5 rusila ceo dijalog.
      (foreach r *FQ-KUCISTA*
        (fq:dcll f (strcat "    : radio_button { key = \"t" (itoa i) "\"; label = \"")
                 (fq:vrime (car r)) "\"; }")
        (setq i (1+ i)))
      (write-line "   }" f)
      (write-line "  }" f)
      (write-line "  : column {" f)
      ;; Model je edit_box, ne padajuca lista: isporuceni katalog nema
      ;; nijedan model, pa lista sama po sebi ne nudi nista.  Padajuca
      ;; lista pored njega vraca ono sto si vec upisivao za ovu vrstu, i
      ;; zasivljena je dok u katalogu nema nijednog zapisa.
      (fq:dcll f "   : boxed_row { label = \"" (fq:t "dcl.kuc.model") "\";")
      (write-line "    : edit_box { key = \"mod\"; edit_width = 24; }" f)
      (write-line "    : popup_list { key = \"modl\"; width = 14; }" f)
      (write-line "   }" f)
      (write-line "   : row {" f)
      (write-line "    : column {" f)
      (fq:dcll f "     : text { label = \"" (fq:t "dcl.kapacitet") "\"; }")
      (write-line "     : popup_list { key = \"kap\"; width = 10; }" f)
      (write-line "    }" f)
      (write-line "    : column {" f)
      (fq:dcll f "     : text { label = \"" (fq:t "dcl.kuc.konektor") "\"; }")
      (write-line "     : popup_list { key = \"kon\"; width = 14; }" f)
      (write-line "    }" f)
      (write-line "    : column {" f)
      (fq:dcll f "     : text { label = \"" (fq:t "dcl.kuc.montaza") "\"; }")
      (write-line "     : popup_list { key = \"mnt\"; width = 18; }" f)
      (write-line "    }" f)
      (write-line "   }" f)
      (write-line "   : row {" f)
      (fq:dcll f "    : edit_box { key = \"ozn\"; label = \"" (fq:t "dcl.oznaka") "\";")
      (write-line "                 edit_width = 12; }" f)
      (fq:dcll f "    : edit_box { key = \"isk\"; label = \"" (fq:t "dcl.kuc.iskorisceno") "\";")
      (write-line "                 edit_width = 6; }" f)
      (write-line "   }" f)
      (write-line "   : row {" f)
      (fq:dcll f "    : edit_box { key = \"lm\"; label = \"" (fq:t "dcl.kuc.letva") "\";")
      (write-line "                 edit_width = 12; }" f)
      (fq:dcll f "    : edit_box { key = \"nm\"; label = \"" (fq:t "dcl.kuc.modula") "\";")
      (write-line "                 edit_width = 4; }" f)
      (write-line "   }" f)
      (fq:dcll f "   : edit_box { key = \"adr\"; label = \"" (fq:t "dcl.kuc.adresa") "\";")
      (write-line "                edit_width = 26; }" f)
      (write-line "   : row {" f)
      (write-line "    : toggle { key = \"spl\";" f)
      (fq:dcll f "               label = \"" (fq:t "dcl.kuc.spliter") "\"; }")
      (write-line "    : popup_list { key = \"spk\"; width = 8; }" f)
      (write-line "   }" f)
      (write-line "  }" f)
      (write-line " }" f)
      ;; Orijentacija je do sada stajala samo u dijalogu kabla, pa se
      ;; samostalno postavljen element mogao okrenuti jedino tako sto
      ;; se prvo nacrta kabl u zeljenom smeru.  Ista lista i isti
      ;; kljucevi kao tamo, iz *FQ-ORI*.
      (fq:dcll f " : boxed_row { label = \"" (fq:t "dcl.orijentacija") "\";")
      (write-line "  : radio_row {" f)
      (setq i 0)
      (foreach r *FQ-ORI*
        (fq:dcll f (strcat "   : radio_button { key = \"or" (itoa i) "\"; label = \"")
                 (fq:orinaz (cadr r)) "\"; }")
        (setq i (1+ i)))
      (write-line "  }" f)
      (write-line " }" f)
      (write-line " : row {" f)
      (fq:dcll f "  : boxed_row { label = \"" (fq:t "dcl.kuc.kacenje") "\";")
      (write-line "   : radio_row {" f)
      (fq:dcll f "    : radio_button { key = \"kk\"; label = \"" (fq:t "dcl.kuc.na.kabl") "\"; }")
      (fq:dcll f "    : radio_button { key = \"ks\"; label = \"" (fq:t "dcl.kuc.samostalno") "\"; }")
      (write-line "   }" f)
      (write-line "  }" f)
      (fq:dcll f "  : boxed_row { label = \"" (fq:t "dcl.status") "\";")
      (write-line "   : radio_row {" f)
      (fq:dcll f "    : radio_button { key = \"stp\"; label = \"" (fq:t "dcl.status.proj") "\"; }")
      (fq:dcll f "    : radio_button { key = \"stz\"; label = \"" (fq:t "dcl.status.izv") "\"; }")
      (write-line "   }" f)
      (write-line "  }" f)
      (write-line " }" f)
      (fq:dcll f " : boxed_column { label = \"" (fq:t "dcl.ispis") "\";")
      (write-line "  : text { key = \"pre\"; label = \"\"; width = 56; }" f)
      (write-line " }" f)
      (write-line " : spacer { height = 0.3; }" f)
      (write-line " : row {" f)
      (fq:dcll f "  : button { key = \"accept\"; label = \"" (fq:t "dcl.ok") "\";")
      (write-line "             is_default = true; width = 14; }" f)
      (fq:dcll f "  : button { key = \"cancel\"; label = \"" (fq:t "dcl.odustani") "\";")
      (write-line "             is_cancel = true; width = 14; }" f)
      (write-line " }" f)
      (write-line "}" f)
      (close f)
      fn)
    nil))

;;; --- punjenje lista i osvezavanje ---------------------------------

(defun fq:kucfill (kljuc lst)
  (start_list kljuc)
  (foreach x lst (add_list x))
  (end_list)
  T)

(defun fq:kucsplk ()
  (fq:kucfill "spk" '("1:2" "1:4" "1:8" "1:16" "1:32" "1:64"))
  T)

;; Puni sve liste i pali ili gasi polja za tekucu vrstu.
(defun fq:kucosv ( / tip kaps splok odf mods)
  (setq tip   *fq-kuc-tip*
        kaps  (fq:kc-kap tip)
        mods  (fq:kc-mod tip)
        odf   (= (fq:kc-crt tip) "odf"))
  ;; Prazan katalog dobija natpis umesto prazne liste, i zasivi se.
  ;; Dva razloga: prazan popup_list je siva mrlja bez objasnjenja, a
  ;; set_tile na indeks u praznoj listi nema sta da izabere.  Natpis
  ;; se nikad ne cita nazad -- iz liste se uzima samo dok je upaljena,
  ;; a upaljena je samo kada mods stvarno postoji.
  (fq:kucfill "modl" (if mods mods (list (fq:t "dcl.kuc.nema.modela"))))
  (fq:kucfill "kap" (mapcar 'itoa kaps))
  ;; Montaza se PRIKAZUJE prevedeno, a pamti se naziv iz kataloga:
  ;; action_tile "mnt" uzima (nth idx (fq:kc-mnt tip)), dakle iz samog
  ;; kataloga, pa prevod dira jedino ono sto se vidi.  Redosled mora da
  ;; ostane isti -- na njemu pociva indeks.  fq:kodnaz nepoznato
  ;; propusta, pa korisnicka montaza prolazi nepromenjena.
  (fq:kucfill "mnt" (mapcar '(lambda (x) (fq:kodnaz "mnt" x)) (fq:kc-mnt tip)))
  (mode_tile "modl" (if mods 0 1))
  (set_tile "modl" (if mods (itoa (fq:kc-poz (fq:kc-zmod tip) mods)) "0"))
  (set_tile "mod" (fq:kc-zmod tip))
  (set_tile "mnt" (itoa (fq:kc-poz (fq:kc-zmnt tip) (fq:kc-mnt tip))))
  (if (not *fq-kuc-kap*) (setq *fq-kuc-kap* (car kaps)))
  (if (not (member *fq-kuc-kap* kaps)) (setq *fq-kuc-kap* (car kaps)))
  (set_tile "kap" (itoa (fq:kc-poz *fq-kuc-kap* kaps)))
  (setq splok (fq:kc-splok tip *fq-kuc-kap*))
  ;; letva i modul, i broj modula, postoje samo kod ODF i patch panela
  (mode_tile "lm"  (if odf 0 1))
  (mode_tile "nm"  (if odf 0 1))
  ;; adresa ima smisla kod ormara i kutija, ne kod letve u rack-u
  (mode_tile "adr" (if odf 1 0))
  (mode_tile "spl" (if splok 0 1))
  (mode_tile "spk" (if (and splok (= (get_tile "spl") "1")) 0 1))
  (fq:kucprev)
  T)

;; Red teksta koji pokazuje sta ce tacno pisati u crtezu.
;; Oznaka se sklapa istim pravilom kao fq:kucoz, da pregled i crtez ne
;; kazu razlicito kada korisnik sam upise oznaku.
(defun fq:kucprev ( / tip s pre nm mod)
  (setq tip *fq-kuc-tip*
        pre (fq:vrpre tip)
        s   (get_tile "ozn")
        ;; iz polja, ne iz *fq-kuc-mod*: pregled mora da prati kucanje
        mod (get_tile "mod")
        nm  (fq:kucnm (get_tile "nm")))
  (set_tile "pre"
    (strcat (cond ((= s "") (strcat pre "<auto>"))
                  ((and (>= (strlen s) (strlen pre))
                        (= (strcase (substr s 1 (strlen pre)))
                           (strcase pre))) s)
                  (T (strcat pre s)))
            (if (= mod "") "" (strcat "   " mod))
            "   " (itoa *fq-kuc-kap*) "x" *fq-kuc-kon*
            "   " (fq:kodnaz "mnt" (fq:kc-zmnt tip))
            (if (and (= (fq:kc-crt tip) "odf") (> nm 1))
              (fq:tf "dcl.kuc.pre.moduli"
                     (list (itoa nm) (itoa (* nm *fq-kuc-kap*))))
              "")
            (if (and (fq:kc-splok tip *fq-kuc-kap*)
                     (= (get_tile "spl") "1"))
              (fq:tf "dcl.kuc.pre.spliter" (list (itoa *fq-kuc-spl*)))
              "")))
  T)

;; Broj modula iz polja: prazno ili besmisleno znaci jedan modul.
(defun fq:kucnm (s / n)
  (setq n (if (= s "") 1 (atoi s)))
  (if (or (< n 1) (> n 24)) 1 n))


;;; ------------------------------------------------------------------
;;;  7. FQKUC, dijalog za sva kucista
;;; ------------------------------------------------------------------

;;  Sivljenje cele grupe Orijentacija.
;;
;;  Orijentacija ima smisla samo kad kuciste stoji SAMOSTALNO.  Uz
;;  "Na kabl" -- a to je podrazumevano -- fq:kucpost ugao uzima iz
;;  KABLA, pa se izbor iz dijaloga tiho ignorise: korisnik izabere
;;  Vodoravno, dobije uspravno kuciste i nema nijednog traga zasto.
;;  Ponasanje je ispravno (kuciste mora da prati kabl), prikaz nije bio.
;;
;;  Isti obrazac kojim se u istom dijalogu sivi spisak odnosa dok
;;  spliter nije ukljucen (action_tile "spl"), samo nad grupom: kljucevi
;;  su racunati ("or0", "or1", ...), pa se prolazi kroz *FQ-ORI* umesto
;;  da se nabrajaju.
;;
;;  n je 1 za sivo, 0 za zivo -- redosled koji trazi mode_tile.
(defun fq:orimode (n / i)
  (setq i 0)
  (foreach o *FQ-ORI*
    (mode_tile (strcat "or" (itoa i)) n)
    (setq i (1+ i))))

(defun c:FQKUC ( / *error* fn id r i rec tip oz mod kap nm k kon mnt lm adr spl st
                   sam kaps sps)
  (setq *error* fq:err)
  (fq:init)
  (if (not (fq:kc *fq-kuc-tip*)) (setq *fq-kuc-tip* (car (car *FQ-KUCISTA*))))
  (if (not *fq-st*) (setq *fq-st* "stProjektovano"))
  (setq sps '(2 4 8 16 32 64))
  (if (setq fn (fq:dclkuc))
    (progn
      (setq id (load_dialog fn))
      (if (or (not id) (< id 0) (not (new_dialog "fqkuc" id)))
        (princ (fq:t "kuc.dijalog.ne"))
        (progn
          ;; vrsta
          (setq i 0)
          (foreach rec *FQ-KUCISTA*
            (if (= (car rec) *fq-kuc-tip*) (set_tile (strcat "t" (itoa i)) "1"))
            (action_tile (strcat "t" (itoa i))
              (strcat "(setq *fq-kuc-tip* \"" (car rec) "\")(fq:kucosv)"))
            (setq i (1+ i)))

          (fq:kucfill "kon" *FQ-KONEKTORI*)
          (set_tile "kon" (itoa (fq:kc-poz *fq-kuc-kon* *FQ-KONEKTORI*)))
          (fq:kucsplk)
          (set_tile "spk" (itoa (fq:kc-poz *fq-kuc-spl* sps)))
          (set_tile "lm"  *fq-kuc-lm*)
          (set_tile "nm"  (itoa *fq-kuc-nm*))
          (set_tile "adr" *fq-kuc-adr*)
          (set_tile "isk" "")
          (set_tile "kk" "1")
          (set_tile (if (= *fq-st* "stIzvedeno") "stz" "stp") "1")
          ;; Orijentacija se ne upisuje odmah u *fq-ugao*, nego se pamti
          ;; redni broj i upisuje tek na OK, isto kao u dijalogu kabla:
          ;; odustajanje ne sme da promeni tekucu orijentaciju.
          (setq *fq-tmp-ori* (fq:oripos))
          (set_tile (strcat "or" (itoa *fq-tmp-ori*)) "1")
          (setq i 0)
          (foreach rec *FQ-ORI*
            (action_tile (strcat "or" (itoa i))
                         (strcat "(setq *fq-tmp-ori* " (itoa i) ")"))
            (setq i (1+ i)))
          ;; Kacenje je gore postavljeno na "kk", pa grupa krece siva.
          (fq:orimode 1)
          (action_tile "kk" "(fq:orimode 1)")
          (action_tile "ks" "(fq:orimode 0)")
          (fq:kucosv)

          ;; Dva puta do iste vrednosti: kucanje u polje i biranje iz
          ;; liste ranije upisanih.  Lista samo prepisuje u polje, pa
          ;; je polje jedini izvor istine -- i za pregled i za OK.
          ;;
          ;; get_tile, a NE $value.  DCL $value ubacuje sadrzaj polja u
          ;; izraz kao string sa navodnicima, pa bi model sa navodnikom
          ;; u imenu -- a to je tacno ono sto se upisuje za rack, 19" --
          ;; razbio izraz na tri dela i eval bi pao usred dijaloga.
          ;; Kod popup_list-a je $value uvek broj, pa tamo ostaje.
          (action_tile "mod"
            (strcat "(setq *fq-kuc-mod* (fq:kc-set *fq-kuc-mod* *fq-kuc-tip*"
                    " (get_tile \"mod\")))(fq:kucprev)"))
          (action_tile "modl"
            (strcat "(set_tile \"mod\" (nth (atoi $value) (fq:kc-mod *fq-kuc-tip*)))"
                    "(setq *fq-kuc-mod* (fq:kc-set *fq-kuc-mod* *fq-kuc-tip*"
                    " (get_tile \"mod\")))(fq:kucprev)"))
          (action_tile "mnt"
            "(setq *fq-kuc-mnt* (fq:kc-set *fq-kuc-mnt* *fq-kuc-tip* (nth (atoi $value) (fq:kc-mnt *fq-kuc-tip*))))(fq:kucprev)")
          (action_tile "kap"
            "(setq *fq-kuc-kap* (nth (atoi $value) (fq:kc-kap *fq-kuc-tip*)))(fq:kucosv)")
          (action_tile "kon"
            "(setq *fq-kuc-kon* (nth (atoi $value) *FQ-KONEKTORI*))(fq:kucprev)")
          (action_tile "spk"
            "(setq *fq-kuc-spl* (nth (atoi $value) '(2 4 8 16 32 64)))(fq:kucprev)")
          (action_tile "spl"
            "(mode_tile \"spk\" (if (= $value \"1\") 0 1))(fq:kucprev)")
          (action_tile "ozn" "(fq:kucprev)")
          (action_tile "nm"  "(fq:kucprev)")
          (action_tile "stp" "(setq *fq-st* \"stProjektovano\")")
          (action_tile "stz" "(setq *fq-st* \"stIzvedeno\")")

          ;; Model se cita i na OK: edit_box javlja promenu tek kad se
          ;; iz njega izadje, a klik pravo na OK iz polja ne izadje.
          (action_tile "accept"
            (strcat "(setq *fq-kuc-mod*"
                    " (fq:kc-set *fq-kuc-mod* *fq-kuc-tip* (get_tile \"mod\")))"
                    "(setq *fq-kuc-lm*  (get_tile \"lm\")"
                    "      *fq-kuc-nm*  (fq:kucnm (get_tile \"nm\"))"
                    "      *fq-kuc-adr* (get_tile \"adr\"))"
                    "(setq *fq-tmp-ozn* (get_tile \"ozn\")"
                    "      *fq-tmp-isk* (get_tile \"isk\")"
                    "      *fq-tmp-spl* (get_tile \"spl\")"
                    "      *fq-tmp-sam* (get_tile \"ks\"))"
                    "(done_dialog 1)"))
          (action_tile "cancel" "(done_dialog 0)")

          (setq r (start_dialog))
          (unload_dialog id)

          (if (= r 1)
            (progn
              ;; Orijentacija vazi za sve sto se dalje crta, isto kao
              ;; kada se izabere u dijalogu kabla -- ali SAMO ako je
              ;; grupa uopste bila ziva.
              ;;
              ;; Sivo polje ne sme da ima dejstvo.  Korisnik koji
              ;; izabere Samostalno, postavi Uspravno, pa se predomisli
              ;; i vrati na "Na kabl", ostavlja *fq-tmp-ori* na 1: bez
              ;; ovog uslova bi mu se SLEDECI element zarotirao po
              ;; vrednosti koju u trenutku OK-a nije mogao ni da vidi
              ;; kao aktivnu.
              (if (= *fq-tmp-sam* "1")
                (setq *fq-ugao* (cadr (nth *fq-tmp-ori* *FQ-ORI*))))
              (setq tip  *fq-kuc-tip*
                    kap  *fq-kuc-kap*
                    kon  *fq-kuc-kon*
                    ;; upisan model ulazi u katalog te vrste, pa ga
                    ;; sledeci put nudi lista, a FQKATSAVE ga sacuva
                    mod  (fq:kc-addmod tip (fq:kc-zmod tip))
                    mnt  (fq:kc-zmnt tip)
                    lm   *fq-kuc-lm*
                    adr  *fq-kuc-adr*
                    st   *fq-st*
                    sam  (= *fq-tmp-sam* "1")
                    spl  (if (and (= *fq-tmp-spl* "1")
                                  (fq:kc-splok tip kap))
                           *fq-kuc-spl* nil)
                    oz   (fq:kucoz tip *fq-tmp-ozn*)
                    nm   (if (= (fq:kc-crt tip) "odf") *fq-kuc-nm* 1)
                    ;; prazno polje znaci: koliko kabl ima vlakana.
                    ;; fq:acel vraca nil i za polje koje nije broj, pa
                    ;; "12a" daje isto sto i prazno -- a ne 12, kao sto
                    ;; je davao atoi.  Naredni red ionako klampuje k na
                    ;; opseg, pa je nil ovde bezbedniji od pogadjanja.
                    k    (fq:acel *fq-tmp-isk* nil))
              (if spl (setq k spl))
              (if k (setq k (max 1 (min k (* kap nm)))))
              (fq:kucpost tip oz mod kap nm k kon mnt lm adr spl st sam))))))
    (princ (fq:t "opste.dcl.ne")))
  (princ))


;;; ------------------------------------------------------------------
;;;  8. PRESETI ZA TRAKU
;;;
;;;  Dugme na traci ne pita nista: uzima model, konektor i montazu iz
;;;  poslednjeg izbora, a kapacitet iz samog dugmeta.
;;; ------------------------------------------------------------------

(defun fq:pkuc (tip kap / oz mod mnt kon k spl nm)
  (fq:init)
  (if (not (fq:kc tip))
    (princ (fq:tf "kuc.nepoznata.vrsta" (list tip)))
    (progn
      (setq mod (fq:kc-zmod tip)
            mnt (fq:kc-zmnt tip)
            kon *fq-kuc-kon*
            oz  (fq:next (fq:vrpre tip))
            nm  (if (= (fq:kc-crt tip) "odf") *fq-kuc-nm* 1)
            spl nil
            k   nil)      ; nil znaci: uzmi koliko kabl ima vlakana
      ;; Spliter po KATALOGU, ne po imenu vrste.
      ;;
      ;; Uslov je do 22.08.2026 glasio (and (= tip "ZOK") (fq:kc-splok
      ;; tip kap)) -- drugi deo je vec pitao katalog, a prvi ga je
      ;; ponistavao za sve osim kutije.  A katalog za ODO i MDU nosi
      ;; sedmo polje 12, dakle "od dvanaest portova navise sme spliter",
      ;; isto kao ZOK.  Dugme je zato polagalo prazan ormar iako je
      ;; tabela pored njega govorila suprotno; jedini put do splitera
      ;; bio je dijalog "Sve vrste", sa stiklom.
      ;;
      ;; Sada odlucuje samo katalog.  ODF i patch panel imaju nulu i
      ;; ispadaju sami, bez ijednog imena vrste u uslovu -- a to je i
      ;; smisao sedmog polja: da se nova vrsta doda u katalog, ne u kod.
      ;;
      ;; Odnos bira fq:splfit, najveci koji staje uz napojni port: FDH 12
      ;; dobija 1:8, 24 -> 1:16, 48 -> 1:32, 72 i 96 -> 1:64.  Ko hoce
      ;; prazan ormar ili drugi odnos, ide kroz dijalog.
      (if (fq:kc-splok tip kap)
        (setq spl (fq:splfit kap)
              k   spl))
      (setq *fq-kuc-tip* tip
            *fq-kuc-kap* kap)
      (fq:kucpost tip oz mod kap nm k kon mnt *fq-kuc-lm* *fq-kuc-adr*
                  spl *fq-st* nil)))
  (princ))

;; ZOK, Patch panel i MDU preseti
(defun c:FQZOK1 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 1))
(defun c:FQZOK2 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 2))
(defun c:FQZOK4 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 4))
(defun c:FQZOK6 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 6))
(defun c:FQZOK8 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 8))
(defun c:FQZOK12 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 12))
(defun c:FQZOK16 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 16))
(defun c:FQZOK24 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "ZOK" 24))

(defun c:FQPP12 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "PP" 12))
(defun c:FQPP24 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "PP" 24))
(defun c:FQPP48 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "PP" 48))
(defun c:FQPP72 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "PP" 72))
(defun c:FQPP96 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "PP" 96))
(defun c:FQPP144 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "PP" 144))

;; MDU ormar.  Traka i dokumentacija zovu komande FQMDU*, ali FQIPAN*
;; ostaju zauvek: stoje u tudjim skriptama i u navici, a i dalje su
;; jedino ime pod kojim je ovaj ormar postojao do verzije 1.0.
(defun c:FQMDU12 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "IPAN" 12))
(defun c:FQMDU24 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "IPAN" 24))
(defun c:FQMDU48 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "IPAN" 48))
(defun c:FQMDU72 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "IPAN" 72))
(defun c:FQMDU96 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "IPAN" 96))
(defun c:FQMDU144 ( / *error*)
  (setq *error* fq:err) (fq:pkuc "IPAN" 144))

(defun c:FQIPAN12 ( / *error*)
  (setq *error* fq:err) (c:FQMDU12))
(defun c:FQIPAN24 ( / *error*)
  (setq *error* fq:err) (c:FQMDU24))
(defun c:FQIPAN48 ( / *error*)
  (setq *error* fq:err) (c:FQMDU48))
(defun c:FQIPAN72 ( / *error*)
  (setq *error* fq:err) (c:FQMDU72))
(defun c:FQIPAN96 ( / *error*)
  (setq *error* fq:err) (c:FQMDU96))
(defun c:FQIPAN144 ( / *error*)
  (setq *error* fq:err) (c:FQMDU144))

;; Montaza je lepljivo podesavanje, isto kao status i orijentacija:
;; postavi se jednom, pa je svi sledeci ormari nose dok se ne promeni.
(defun fq:setmnt (tip mnt)
  (setq *fq-kuc-mnt* (fq:kc-set *fq-kuc-mnt* tip mnt))
  ;; Pamti se ono sto je stiglo (naziv iz kataloga), a POKAZUJE prevod.
  ;; Katalog je jezicki neutralan i ostaje na srpskim nazivima -- vidi
  ;; komentar uz domen "mnt" u fq-sema.lsp.
  (princ (fq:tf "kuc.montaza" (list (fq:vrime tip) (fq:kodnaz "mnt" mnt))))
  (princ))

(defun c:FQMNTU ( / *error*)
  (setq *error* fq:err) (fq:setmnt "ODO" "unutrasnji"))
(defun c:FQMNTS ( / *error*)
  (setq *error* fq:err) (fq:setmnt "ODO" "spoljasnji"))
(defun c:FQMNTM ( / *error*)
  (setq *error* fq:err) (fq:setmnt "ODO" "samostojeci"))


;;; ------------------------------------------------------------------
;;;  9. KOMANDNA LINIJA
;;;
;;;  Za skripte i za rad bez dijaloga.  Crtica na pocetku imena je
;;;  ista konvencija koju koristi i sam AutoCAD, LAYER prema -LAYER.
;;; ------------------------------------------------------------------

(defun fq:kucdash (tip / oz mod kap nm k kon mnt lm adr spl st sam kw kaps s)
  (fq:init)
  (setq kaps (fq:kc-kap tip))
  ;; Isto pitanje kao u fq:odoask i fq:odfask, i ista niska: naziv
  ;; elementa je umetak, pa tri komande ne drze tri recenice.
  (setq sam (fq:dvatok "opste.tok.kabl" "opste.tok.samostalno"
                       "opste.pitaj.kaci" (list (fq:vrime tip))))
  ;; getint prima i 0 i negativan broj, a prazan kapacitet posle daje
  ;; prazan raspored konektora i prekid crtanja
  (if (or (not (setq kap (getint (fq:tf "kuc.pitaj.kapacitet" (list (itoa (car kaps)))))))
          (< kap 1))
    (setq kap (car kaps)))
  (setq oz (fq:kucoz tip (getstring T (fq:t "spl.pitaj.oznaka"))))
  ;; Prazan katalog daje "<bez>", ne "<>": prazne uglaste zagrade lice
  ;; na gresku u ispisu.  Upisan model se pamti i dopisuje u katalog,
  ;; isto kao iz dijaloga -- skripta i dijalog ne smeju da se razlikuju.
  (setq mod (getstring T (fq:tf "kuc.pitaj.model" (list (if (= (fq:kc-zmod tip) "") (fq:t "kuc.model.bez") (fq:kc-zmod tip))))))
  (if (= mod "")
    (setq mod (fq:kc-zmod tip))
    (progn (setq *fq-kuc-mod* (fq:kc-set *fq-kuc-mod* tip mod))
           (fq:kc-addmod tip mod)))
  (setq kon (getstring T (fq:tf "odf.pitaj.konektor" (list *fq-kuc-kon*))))
  (if (= kon "") (setq kon *fq-kuc-kon*))
  ;; Prompt pokazuje prevod, a Enter zadrzava vrednost IZ KATALOGA.
  ;; Sme da se razlikuju: fq:nazkod poredi sve cetiri kolone, pa i
  ;; otkucano "indoor" i zadrzano "unutrasnji" daju isti kod mtIndoor.
  ;; Nabrajanje nije zatvoreno, pa otkucano "u sahtu" prolazi doslovno.
  (setq mnt (getstring T (fq:tf "kuc.pitaj.montaza" (list (fq:kodnaz "mnt" (fq:kc-zmnt tip))))))
  (if (= mnt "") (setq mnt (fq:kc-zmnt tip)))
  (setq lm  *fq-kuc-lm*
        adr *fq-kuc-adr*
        nm  1
        spl nil)
  (if (= (fq:kc-crt tip) "odf")
    (progn
      (setq lm (getstring T (fq:tf "kuc.pitaj.letva" (list *fq-kuc-lm*))))
      (if (= lm "") (setq lm *fq-kuc-lm*))
      (if (not (setq nm (getint (fq:t "kuc.pitaj.moduli")))) (setq nm 1))
      (setq nm (max 1 (min nm 24))))
    (progn
      (setq adr (getstring T (fq:t "kuc.pitaj.adresa")))))
  ;; Kutija od 12 nagore se i pravi zbog splitera, pa je tamo
  ;; podrazumevano Da.  Ormar je pre svega razdelnik, spliter je u
  ;; njemu izuzetak, pa je tamo podrazumevano Ne.
  (if (fq:kc-splok tip kap)
    (progn
      ;;  PODRAZUMEVANO ZAVISI OD VRSTE, pa se red tokena obrce.
      ;;  fq:dvatok po ugovoru podrazumeva PRVI token, a ovde je to
      ;;  kod kutije "Da" a kod ormara "Ne".  Dva poziva sa zamenjenim
      ;;  redom su jeftinija od zastavice koja bi vazila svuda.
      ;;
      ;;  Do bloka 4 je isto pravilo stajalo na TRI mesta u jednom
      ;;  izrazu -- rec za vrstu, slovo u zagradi i podrazumevan token --
      ;;  i sva tri su morala da se menjaju zajedno.
      (setq kw (if (= (fq:kc-crt tip) "zok")
                 (not (fq:dvatok "opste.tok.da" "opste.tok.ne"
                                 "kuc.pitaj.spliter"
                                 (list (fq:t "kuc.kutije"))))
                 (fq:dvatok "opste.tok.ne" "opste.tok.da"
                            "kuc.pitaj.spliter"
                            (list (fq:t "kuc.ormara")))))
      (if kw
        (progn
          (if (not (setq s (getint (fq:tf "kuc.pitaj.odnos" (list (itoa (fq:splfit kap)))))))
            (setq s (fq:splfit kap)))
          (setq spl s *fq-kuc-spl* s)))))
  (if spl
    (setq k spl)
    (if (not (setq k (getint (fq:tf "odf.pitaj.konektori" (list (itoa (min 6 (* kap nm))))))))
      (setq k (min 6 (* kap nm)))))
  (setq k  (max 1 (min k (* kap nm)))
        st (fq:askst)
        *fq-kuc-kon* kon
        *fq-kuc-lm*  lm
        *fq-kuc-adr* adr)
  ;; nm je za ostale vrste fiksirano na 1, pa bi upis obrisao zapamcen
  ;; broj modula ODF-a
  (if (= (fq:kc-crt tip) "odf") (setq *fq-kuc-nm* nm))
  (fq:kucpost tip oz mod kap nm k kon mnt lm adr spl st sam))

(defun c:-FQZOK ( / *error*)
  (setq *error* fq:err) (fq:kucdash "ZOK")  (princ))
(defun c:-FQPP ( / *error*)
  (setq *error* fq:err) (fq:kucdash "PP")   (princ))
(defun c:-FQMDU ( / *error*)
  (setq *error* fq:err) (fq:kucdash "IPAN") (princ))
(defun c:-FQIPAN ( / *error*)
  (setq *error* fq:err) (c:-FQMDU))
(defun c:-FQODO ( / *error*)
  (setq *error* fq:err) (fq:kucdash "ODO")  (princ))
(defun c:-FQODF ( / *error*)
  (setq *error* fq:err) (fq:kucdash "ODF")  (princ))

;; kratke komande bez crtice vode na dijalog, otvoren na toj vrsti.
;; FQODF i FQODO su vec zauzeti starim komandama iz fq-sema.lsp, pa
;; ODF i ODO dobijaju imena sa nastavkom DLG.
(defun c:FQZOK ( / *error*)
  (setq *error* fq:err) (setq *fq-kuc-tip* "ZOK")  (c:FQKUC))
(defun c:FQPP ( / *error*)
  (setq *error* fq:err) (setq *fq-kuc-tip* "PP")   (c:FQKUC))
(defun c:FQMDU ( / *error*)
  (setq *error* fq:err) (setq *fq-kuc-tip* "IPAN") (c:FQKUC))
(defun c:FQIPAN ( / *error*)
  (setq *error* fq:err) (c:FQMDU))
(defun c:FQODFDLG ( / *error*)
  (setq *error* fq:err) (setq *fq-kuc-tip* "ODF")  (c:FQKUC))
(defun c:FQODODLG ( / *error*)
  (setq *error* fq:err) (setq *fq-kuc-tip* "ODO")  (c:FQKUC))


;;; ------------------------------------------------------------------
;;;  9b. DODAVANJE VLAKNA NA POSTOJECE KUCISTE I SIRENJE RAMA
;;;
;;;  Situacija iz prakse: ODO je postavljen sa cetiri iskoriscena porta
;;;  (48, 47, 46, 45), pa se posle nekog vremena na port 44 kaci jos
;;;  jedno vlakno istog kabla.
;;;
;;;  Umesto da se docrtava jedan port i rucno rasteze ram, kuciste se
;;;  PRECRTAVA sa novim brojem iskoriscenih konektora.  Tako se ram,
;;;  pregrada, natpisi i numeracija uvek slazu medju sobom, i isti
;;;  postupak vazi za sve vrste kucista.  Stari podaci se citaju iz
;;;  xdata zapisa rama, pa se nista ne pita dva puta.
;;;
;;;  Isti posao radi i AUTOMATSKO sirenje, fq:kucsirikabl, koje se
;;;  okida odmah po crtanju kabla.  Zato je citav postupak u jezgru
;;;  fq:kucspoji, a FQDODAJ i automatika se razlikuju samo u tome da li
;;;  se pita koliko vlakana da se poveze:
;;;
;;;    FQDODAJ       pita, podrazumevano povezuje, 0 znaci samo sirenje
;;;    automatika    ne pita, ne povezuje nista novo, samo siri ram
;;;
;;;  Automatika je potrebna jer element cesto ide u crtez PRE kabla.
;;;  Tada je njegov ram visok koliko i broj zauzetih konektora, jer se
;;;  vise od toga u tom trenutku i ne zna, pa kabl koji posle krene iz
;;;  njega donese ceo snop i vlakna preko toga vire iz rama.
;;;
;;;  Brisanje starog crteza ide prozorom preko rama, gde entitet mora da
;;;  bude CEO unutar prozora.  Vlakna iz rama izlaze pa ne mogu da budu
;;;  obuhvacena, a filter po lejerima ostavlja oznake kabla, cevcice i
;;;  sve ostalo netaknuto.
;;;
;;;  Prozor se NE bira preko ssget "_W" nego se meri rucno; zasto, stoji
;;;  uz fq:kucbrisi.
;;; ------------------------------------------------------------------

(setq *FQ-KUC-LEJERI* "FQ_ODF,FQ_ODO,FQ_ZOK,FQ_SPLITER")

;; Vrsta kucista iz xdata zapisa, ili nil ako to nije kuciste.
(defun fq:kucvrsta (d)
  (if (and d (member (car d) '("ODF" "PP" "ODO" "IPAN" "ZOK")))
    (car d)))

;; Ram kucista koji je korisnik pokazao.  Vraca (entitet . xdata).
(defun fq:kucpick (por / s d)
  (setq s (entsel por))
  (cond
    ((not s) (princ (fq:t "opste.nista")) nil)
    ((not (fq:kucvrsta (setq d (fq:xdget (car s)))))
       (princ (fq:t "kuc.nije.ram"))
       (princ (fq:t "kuc.klikni.ram"))
       nil)
    (T (cons (car s) d))))

;; Rep liste od n-tog elementa nadalje, n je 1-baziran.
(defun fq:podlista (l n / i out)
  (setq i 1 out nil)
  (foreach x l
    (if (>= i n) (setq out (cons x out)))
    (setq i (1+ i)))
  (reverse out))

;; fq:txtpt je preseljen u fq-sema.lsp, uz fq:txtpom: isti propust je
;; drzao i natpise nastavka na mestu dok im ram bezi.

;; Tacke po kojima se meri da li je entitet unutar prozora, u sistemu
;; kabla.  Svaka mora da bude u prozoru da bi entitet bio obrisan.
;;
;;   TEXT     jedna tacka, ona umetanja.  Adresa ormara je siri tekst
;;            od samog rama, pa bi merenje po slovima uvek ispadalo
;;            izvan prozora i natpis bi posle precrtavanja ostao dvaput.
;;   ELLIPSE  grupa 11 je VEKTOR poluose, ne tacka, pa se granice
;;            racunaju iz centra i duzine te poluose.
;;   ostalo   sve tacke iz grupa 10 i 11.  Kod luka polilinije to
;;            zanemaruje ispupcenje, a ono je kod svih FiberQ simbola
;;            manje od desetine jedinice.
(defun fq:etacke (d / tip out x c r)
  (setq tip (cdr (assoc 0 d)))
  (cond
    ((= tip "TEXT") (list (fq:loc (fq:txtpt d))))
    ((= tip "ELLIPSE")
       (setq c (fq:loc (cdr (assoc 10 d)))
             r (distance '(0.0 0.0 0.0) (cdr (assoc 11 d))))
       (list (list (- (car c) r) (- (cadr c) r))
             (list (+ (car c) r) (+ (cadr c) r))))
    (T (setq out nil)
       (foreach x d
         (if (or (= (car x) 10) (= (car x) 11))
           (setq out (cons (fq:loc (cdr x)) out))))
       out)))

;; Brise crtez kucista koji zauzima lokalni pravougaonik ul..ur, vb..vt.
;;
;; gore i dole su koliko prozor sme da predje preko rama.  NISU
;; konstante, i to je ovde bitno: natpisi kucista stoje izvan rama, ali
;; kod slozenog ODF-a ram modula iznad zavrsava tacno na ovom vt, a
;; njegovi donji konektori su svega pola rastera vise.  Prevelik prozor
;; bi ih pojeo, pa se navise ide samo ako iznad NEMA modula.
;;
;; Entitet mora da bude CEO unutar prozora.  Tako se ne dira ram
;; susednog modula, koji jednom ivicom lezi tacno na ovoj granici.
;; Vlakna kroz ram prolaze, ali su na svom lejeru pa u ovaj spisak i ne
;; ulaze.
;;
;; Trazi se kroz "_X" i meri se rucno, NE preko ssget "_W".  Prozorski
;; izbor uzima samo ono sto je u tekucem pogledu, pa se kuciste koje
;; nije celo na ekranu brisalo samo delimicno i posle precrtavanja bi
;; portovi i natpisi ostali dvaput.  To se ne vidi dok se ne odzumira,
;; a od automatskog sirenja se okida i bez ijednog klika na kuciste.
;;
;; Vraca PAR (nasao . obrisao).  Sam broj obrisanih ne kaze nista bez
;; broja nadjenih: 3 obrisana od 3 i 3 od 5 su ista cifra a suprotna
;; ishoda.  Pozivalac crta nov crtez samo kada su jednaki.
(defun fq:kucbrisi (ul ur vt vb gore dole
                    / u1 u2 vg vd tab ss i e d pts p ok n nas)
  (setq u1  (- ul 0.6)
        u2  (+ ur 0.6)
        vg  (+ vt gore)
        vd  (- vb dole)
        ;; NE (getvar "CTAB").  Kada se radi u model prostoru KROZ
        ;; plivajuci viewport, CTAB vraca ime layouta a entiteti nose
        ;; 410 = "Model", pa bi uslov nize pao za SVAKI kandidat: nista
        ;; se ne bi obrisalo, a precrtavanje bi odmah nacrtalo nov ram
        ;; preko starog.  Dupli ram, dupli portovi, dupli natpisi, i dva
        ;; LWPOLYLINE-a sa FIBERQ zapisom na istoj tacki.
        tab (fq:snapprostor)
        n   0
        nas 0
        i   0
        ss  (fq:ssx (list (cons 8 *FQ-KUC-LEJERI*))))
  (if ss
    (while (< i (sslength ss))
      (setq e   (ssname ss i)
            d   (entget e)
            pts (fq:etacke d)
            ok  (and pts
                     ;; ne diraj drugi list, "_X" prolazi kroz sve
                     (or (not (assoc 410 d))
                         (not tab)
                         (= (cdr (assoc 410 d)) tab))))
      (foreach p pts
        (if (not (and (>= (car  p) u1) (<= (car  p) u2)
                      (>= (cadr p) vd) (<= (cadr p) vg)))
          (setq ok nil)))
      ;; Broji se samo ono sto je stvarno nestalo.  Ovo NIJE kozmetika:
      ;; fq:kucprecrtaj na osnovu ovog broja crta nov ram preko starog.
      ;; Ako stari nije obrisan (zakljucan lejer), u crtezu ostaju dva
      ;; rama jedan preko drugog, oba sa svojim xdata zapisom -- pa ih
      ;; specifikacija broji dvaput, a na papiru se ne vide kao dva.
      (if ok
        (progn
          (setq nas (1+ nas))
          (if (fq:brisi e) (setq n (1+ n)))))
      (setq i (1+ i))))
  (cons nas n))

;; Pocetak veze koju kuciste upisuje vlaknima.  ODF i patch panel u
;; adresu stavljaju i letvu i modul ("OR 1 L1, M2/45"), ostali ne
;; ("ODO 1/48").
;;  fq:spoj, ne go strcat: oznaka letve sme da bude prazna, pa bi
;;  "PP 1 /3" nosio razmak koji nista ne razdvaja.  Za nepraznu oznaku
;;  je rezultat doslovno isti kao pre, pa zatecen crtez i dalje nalazi
;;  svoja vlakna.  Isti spoj mora da stoji i u fq:odfmod i u fq:drawodf,
;;  jer se ovde samo cita ono sto oni upisu.
(defun fq:kucvez (oz lm crt)
  (if (= crt "odf") (strcat (fq:spoj oz " " lm) "/") (strcat oz "/")))

;; Vlakna koja su VEC vezana na ovo kuciste, procitana iz veza upisanih
;; u sama vlakna.  Vraca listu istog oblika kakav daje fq:pickcable,
;; (redno-mesto tacka entitet), poredjanu po rednom mestu konektora.
;;
;; Ovo je ono sto omogucava da kuciste primi jos jedan kabl: stanje se
;; ne pamti nigde posebno, nego se rekonstruise iz crteza, pa se posle
;; kuciste precrta sa oba kabla zajedno i ram se sam rashiri.
;;
;; unazad je T za ormare i kutije, gde prvo vlakno ide na POSLEDNJI
;; konektor, pa je redno mesto kap-(broj-1).
(defun fq:kucstari (pre kap unazad u0 / ss i e d j pts p bd dd out res r)
  (setq ss (fq:ssx '((8 . "FQ_VLAKNO") (-3 ("FIBERQ"))))
        i  0
        out nil)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i)
            d (fq:xdget e))
      (if d
        (foreach s (fq:delovi (fq:vl-veza d))
          (if (setq j (fq:posle s pre))
            (progn
              ;; kraj vlakna koji je blizi koloni konektora
              (setq pts (fq:pl10 e) p nil bd nil)
              (foreach q pts
                (setq dd (abs (- (car (fq:loc q)) u0)))
                (if (or (not bd) (< dd bd)) (setq p q bd dd)))
              (setq out (cons (list (if unazad (- kap (1- j)) j) p e)
                              out))))))
      (setq i (1+ i))))
  ;; poredjaj po rednom mestu; assoc uzima prvo, pa dupli broj konektora
  ;; ne moze da udvoji zapis
  (setq res nil i 1)
  (while (<= i kap)
    (if (setq r (assoc i out)) (setq res (cons r res)))
    (setq i (1+ i)))
  (reverse res))

;; Sva vlakna koja ZAVRSAVAJU na koloni konektora u0, u rasponu rama po
;; visini.  Vraca listu istog oblika kao fq:kucstari, (0 tacka entitet),
;; uredjenu odozgo nanize.  Redno mesto je 0 jer ta vlakna ne stoje ni
;; na jednom konektoru: ona u kucistu vise.
;;
;; fq:kucstari cita samo vlakna sa UPISANOM vezom.  Kada u kuciste udje
;; jos jedan kabl, spisak za precrtavanje bi bez ovoga nosio samo vezana
;; vlakna, pa bi sva koja vise ostala bez koluta.
;;
;; Gleda se samo KRAJ vlakna, prvo ili poslednje teme.  Vlakno koje kroz
;; ram samo prolazi nema teme na koloni, pa ovde i ne ulazi.
;;
;; izuz je spisak entiteta koji su vec obuhvaceni.
(defun fq:kucvise (u0 vt vb izuz / ss i e d pts p lc lv bd bv dd out)
  (setq ss  (fq:ssx '((8 . "FQ_VLAKNO") (-3 ("FIBERQ"))))
        i   0
        out nil)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i)
            d (fq:xdget e))
      (if (and d (= (car d) "VLAKNO") (not (member e izuz)))
        (progn
          (setq pts (fq:pl10 e) p nil bd nil bv nil)
          (foreach q (list (car pts) (fq:last pts))
            (setq lc (fq:loc q)
                  dd (abs (- (car lc) u0)))
            (if (or (not bd) (< dd bd)) (setq p q bd dd bv (cadr lc))))
          (if (and p (< bd 1.0)
                   (<= bv (+ vt 0.01)) (>= bv (- vb 0.01)))
            (setq out (cons (cons bv (list 0 p e)) out)))))
      (setq i (1+ i))))
  (if out
    (mapcar 'cdr (vl-sort out '(lambda (a b) (> (car a) (car b)))))
    nil))

;; Da li lista pocinje od 1 i ide bez rupa.  Ako ne ide, precrtavanje sa
;; unijom kablova bi pomerilo konektore, pa se na to ne sme osloniti.
(defun fq:kucredom (l / i ok)
  (setq i 1 ok T)
  (foreach r l
    (if (/= (car r) i) (setq ok nil))
    (setq i (1+ i)))
  ok)

;;  Najnizi red na kome unutar rama stoji izlaz splitera, ili nil kad
;;  izlaza nema.
;;
;;  Cita se iz same lepeze: linije izlaza nose xdata "IZLAZ", a jedan
;;  njihov kraj lezi tacno na portu.  Telo je na sredini raspona, pa
;;  minimum po SVIM temenima daje isti odgovor kao minimum po krajevima
;;  na portovima -- a ne mora da se pogadja koji je kraj koji.
;;
;;  fq:etacke, NE fq:plpts.  fq:plpts ide kroz fq:pl10, koja kupi samo
;;  DXF grupu 10, a lepeza je LINE: grupa 10 je kraj kod TELA, grupa 11
;;  kraj na PORTU (fq:odospl i fq:zokspl oba pisu (fq:Wline su2 vmid kl
;;  v ...)).  Svih n grana lepeze zato ima isto teme 10 -- sredinu tela
;;  -- pa je ova funkcija vracala vmid umesto najnizeg reda izlaza,
;;  dakle SREDINU lepeze.  U ormaru 48 sa spliterom 1:32 je to bilo
;;  13.2 umesto 0.8.
;;
;;  Kvar se video samo posredno i tek na crtezu: fq:porram od toga
;;  racuna red poravnanja sledeceg kabla, pa je privodni kabl padao
;;  tacno na donju polovinu lepeze -- bas ona greska zbog koje je
;;  napojni port i spusten dole (vidi odeljak 3c).  Nijedna staticka
;;  provera to ne hvata: fq:plpts je ispravna funkcija, samo za drugu
;;  vrstu entiteta.
;;
;;  Sluzi racunu poravnanja: prvi red rama kod kucista sa spliterom
;;  nije konektor kabla nego izlaz 1, pa se poravnanje mora spustiti
;;  ispod cele lepeze.  Koliko ispod -- to zavisi od vrste i racuna se u
;;  fq:splpor, uz fq:porram -- kod ormara jedan raster, kod ZOK kutije
;;  ceo blok slobodnih konektora izmedju lepeze i napajanja.
(defun fq:izlnaj (ul ur vt vb / ss i e d r rb)
  (setq ss (fq:ssx '((8 . "FQ_SPLITER") (-3 ("FIBERQ")))) i 0 r nil rb 0)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i) d (fq:xdget e))
      (if (and d (= (car d) "IZLAZ"))
        (foreach p (fq:etacke (entget e))
          (if (and (>= (car  p) (- ul 0.6)) (<= (car  p) (+ ur 0.6))
                   (>= (cadr p) (- vb 0.6)) (<= (cadr p) (+ vt 0.6))
                   (or (not r) (< (cadr p) r)))
            ;; redni broj se cita IZ ISTOG zapisa iz kog i red.  Da se
            ;; broj izlaza umesto toga PREBROJI, obrisana srednja grana
            ;; lepeze bi pomerila samo brojac a ne i red, pa bi racun
            ;; nizeg sloja promasio za tacno toliko rastera -- i to
            ;; nanize, ispod donje ivice rama, gde konektora nema.
            (setq r  (cadr p)
                  rb (atoi (fq:f 2 d))))))
      (setq i (1+ i))))
  (if r (cons r rb)))

;;  Samo red, bez rednog broja -- oblik koji trazi fq:splnova.
(defun fq:izlred (ul ur vt vb / r)
  (if (setq r (fq:izlnaj ul ur vt vb)) (car r)))

;; Ima li unutar rama telo splitera, i koje.
;;
;; Kuciste sa spliterom se ne precrtava.  Telo, lepeza i ulaz nisu
;; posledica spiska vlakana nego zasebna odluka projektanta, pa bi ih
;; precrtavanje obrisalo (FQ_SPLITER je u *FQ-KUC-LEJERI*) a ne bi ih
;; vratilo.  ZOK to zna iz svog zapisa, ormar ne, jer mu je zapis isti
;; sa spliterom i bez njega; zato se gleda sam crtez.
;;
;; Vraca "oznaka odnos" ili nil.
(defun fq:splukut (ul ur vt vb / ss i e d pts ok r)
  (setq ss (fq:ssx '((8 . "FQ_SPLITER") (-3 ("FIBERQ"))))
        i  0
        r  nil)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i) d (fq:xdget e))
      (if (and d (= (car d) "SPLITER") (setq pts (fq:plpts e)))
        (progn
          (setq ok T)
          (foreach p pts
            (if (not (and (>= (car  p) (- ul 0.6)) (<= (car  p) (+ ur 0.6))
                          (>= (cadr p) (- vb 0.6)) (<= (cadr p) (+ vt 0.6))))
              (setq ok nil)))
          (if ok (setq r (strcat (fq:f 1 d) " " (fq:f 2 d))))))
      (setq i (1+ i))))
  r)

;; Da li je entitet vec u listi vlakana.
(defun fq:imae (l e / r)
  (setq r nil)
  (foreach x l (if (eq (caddr x) e) (setq r T)))
  r)

;;  Koliko izlaza splitera lezi unutar rama.
;;
;;  Isti prolaz i isti uslov kao fq:izlred, samo se broji umesto da se
;;  trazi najnizi red.  Broj je potreban da bi se znalo koliko je
;;  portova spliter vec uzeo, pa da dopisan port ne padne na njegov.
;;
;;  fq:etacke iz istog razloga kao tamo: lepeza je LINE, a fq:plpts bi
;;  videla samo kraj kod tela.  Ovde je posledica blaza -- teme kod tela
;;  je uvek unutar rama pa je broj slucajno ispadao tacan -- ali bi
;;  samostalan spliter spusten pored kucista prosao kao njegov.
(defun fq:izlbroj (ul ur vt vb / ss i e d n ok)
  (setq ss (fq:ssx '((8 . "FQ_SPLITER") (-3 ("FIBERQ")))) i 0 n 0)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i) d (fq:xdget e))
      (if (and d (= (car d) "IZLAZ"))
        (progn
          (setq ok T)
          (foreach p (fq:etacke (entget e))
            (if (not (and (>= (car  p) (- ul 0.6)) (<= (car  p) (+ ur 0.6))
                          (>= (cadr p) (- vb 0.6)) (<= (cadr p) (+ vt 0.6))))
              (setq ok nil)))
          (if ok (setq n (1+ n)))))
      (setq i (1+ i))))
  n)

;;  Granice do kojih ram sme da se rastegne da obuhvati snop.
;;
;;  Rastezanje je jedina izmena rama koja NISTA ne brise, pa se sme i
;;  tamo gde se precrtavanje ne usudjuje.  Jedina granica je susedni
;;  modul slozenog razdelnika: on deli ivicu sa nama, pa bi rastegnut
;;  ram usao u tudji.  Isto pravilo i isti par uslova kao u
;;  fq:kucprecrtaj, samo na jednom mestu.
;;
;;  Vraca (vtNovo vbNovo), spremno za fq:kucramsiri.
(defun fq:kucgran (crt oz ang ul ur vt vb vs / svi m)
  (setq m   (fq:kucmarg crt)
        svi (if (= crt "odf") (fq:kucsvi)))
  (list (if (and svi (fq:odfiznad svi oz ang ul ur vt))
          vt
          (max vt (+ (apply 'max vs) m)))
        (if (and svi (fq:odfispod svi oz ang ul ur vb))
          vb
          (min vb (- (apply 'min vs) m)))))

;;  Vlakna kabla koja jos nisu prikljucena na ovo kuciste, odozgo
;;  nanize, i koja leze unutar rama.
;;
;;  pre je pocetak veze koju kuciste upisuje (fq:kucvez).  Vlakno koje
;;  taj pocetak vec nosi stoji na portu i ne sme drugi put -- inace bi
;;  FQBUDZET brojao dva konektorska para na istom kraju.
(defun fq:splnova (fl pre u0 vt vb izl / out d vez lc)
  (setq out nil)
  (foreach r fl
    (setq d   (if (caddr r) (fq:xdget (caddr r)))
          vez nil
          lc  (fq:loc (cadr r)))
    (if d
      (foreach s (fq:delovi (fq:vl-veza d))
        (if (fq:posle s pre) (setq vez T))))
    (if (and d
             (not vez)
             ;; Vlakno mora da ZAVRSAVA na koloni konektora.  FQDODAJ
             ;; kuciste bira klikom, ne po kraju kabla, pa kabl sme da
             ;; bude i onaj koji tuda samo prolazi -- portovi bi se
             ;; nacrtali na koloni, a vlakna bi ostala metrima daleko.
             ;; Isto to, istim brojem, proverava i grana koja precrtava.
             (< (abs (- (car lc) u0)) 1.0)
             (<= (cadr lc) (+ vt 0.01))
             (>= (cadr lc) (- vb 0.01))
             ;; Ispod cele lepeze.  Izlazi splitera stoje IZNAD snopa i
             ;; drze svoje portove; vlakno na njihovom redu bi dobilo
             ;; port nacrtan preko tudjeg, i to bez ijedne poruke.
             (or (not izl) (< (cadr lc) (- izl 0.01))))
      (setq out (cons (cons (cadr lc) r) out))))
  ;; odozgo nanize, jer tako i brojevi idu: kap, kap-1, ...
  (if out
    (mapcar 'cdr (vl-sort out '(lambda (a b) (> (car a) (car b)))))
    nil))

;;;  DOPISIVANJE PORTOVA U ORMAR KOJI DRZI SPLITER
;;;
;;;  Precrtati se ne sme: telo, lepeza i ulaz nisu posledica spiska
;;;  vlakana nego zasebna odluka projektanta, pa bi ih precrtavanje
;;;  obrisalo bez povratka -- isti razlog stoji uz fq:splukut.  Do
;;;  23.08.2026 je zato FQDODAJ nad takvim ormarom umeo samo da
;;;  rastegne ram i javi "nema sta da se doda", i to je bilo tacno
;;;  onoliko koliko i beskorisno: privodni kabl je ulazio u ormar i
;;;  ostajao bez ijednog konektora.
;;;
;;;  Portovi se zato DOPISUJU.  Nista se ne brise: nacrta se port na
;;;  redu na kome vlakno vec lezi i vlaknu se upise veza.  Spliter
;;;  ostaje netaknut jer ga niko nije ni dirao.
;;;
;;;  Brojevi idu unazad od kapaciteta, tacno kako ih fq:drawodospl deli
;;;  vlaknima kabla: napojno nosi kap, sledece kap-1 i tako nanize --
;;;  a nanize i lezi, jer izlazi splitera stoje IZNAD snopa.  Prvih no
;;;  brojeva drze ti izlazi, pa se staje cim se do njih stigne: port 32
;;;  u ormaru od 48 sa spliterom 1:32 je izlaz, i vlakno na njemu bi ga
;;;  tiho prekrilo.
;;;
;;;  Vraca (koliko prviPort poslednjiPort) ili nil.
(defun fq:splkon (oz kap ul ur vt vb u0 fl pitaj
                  / pre no stari nova slob ord pn p1 nn n i rec v)
  (setq pre   (strcat oz "/")
        no    (fq:izlbroj ul ur vt vb)
        stari (fq:kucstari pre kap T u0)
        ;; sledece redno mesto ide IZA poslednjeg zauzetog, ne po broju
        ;; zauzetih: rupa u numeraciji se preskace, ne popunjava --
        ;; inace bi dopisan port dobio broj koji u crtezu vec stoji
        ord   (if stari (1+ (car (fq:last stari))) 1)
        pn    (- kap (1- ord))
        p1    pn
        slob  (max 0 (- pn no))
        nova  (fq:splnova fl pre u0 vt vb (fq:izlred ul ur vt vb))
        nn    (min slob (length nova)))
  (if (< nn 1)
    nil
    (progn
      (setq n (if pitaj (fq:kucpitaj nn nn) nn)
            i 0)
      (while (< i n)
        (setq rec (nth i nova)
              v   (cadr (fq:loc (cadr rec))))
        (fq:port u0 v pn)
        (fq:addveza (caddr rec) (strcat oz "/" (itoa pn)))
        (setq pn (1- pn)
              i  (1+ i)))
      (if (> n 0) (list n p1 (1+ pn))))))

;; Precrtava kuciste sa novom listom vlakana i novim brojem
;; iskoriscenih konektora.
;;
;; Stari crtez se brise po ZATECENIM granicama rama, jer je on tu, a
;; NOVI ram racuna crtac iz same liste vlakana.  Zato se ram sam
;; rashiri kada u listu udje jos jedan kabl: nista se ne rasteze rucno.
;;
;; Kod ODF-a se precrtava samo taj modul; deljena ivica prema susednom
;; modulu ostaje gde je bila, da se moduli iznad i ispod ne pomere.
;;
;; Vraca T ako je precrtano, nil ako je odustalo.  Odustaje se kada
;; stari crtez nije mogao ceo da nestane (zakljucan ili zamrznut lejer):
;; nov ram preko starog daje dva zapisa na istoj tacki, sto je tise i
;; gore od toga da se ne nacrta nista.
(defun fq:kucprecrtaj (tip crt oz mod kap kn kon mnt lm adr st fl2 ang
                       ul ur vt vb / svi hm iznad ispod vtn vbn vg gore br pz)
  (if (= crt "odf")
    (progn
      (setq svi   (fq:kucsvi)
            hm    (fq:odfvis
                    (mapcar '(lambda (r) (fq:loc (cadr r))) fl2) kap)
            iznad (fq:odfiznad svi oz ang ul ur vt)
            ispod (fq:odfispod svi oz ang ul ur vb)
            vtn   (if iznad vt (+ (apply 'max hm) *FQ-ODF-MARG*))
            ;;  Do dna SNOPA, ne do poslednjeg konektora -- ista mera i
            ;;  isti razlog kao u fq:drawodfn: u patch panel od 24 ume
            ;;  da udje kabl od 48, pa bi pola snopa visilo ispod rama.
            ;;  Kada ISPOD stoji jos jedan modul istog razdelnika, ram
            ;;  ostaje gde je bio -- rastegnut bi usao u tudji.
            vbn   (if ispod
                    vb
                    (- (min (apply 'min hm)
                            (apply 'min (mapcar '(lambda (r)
                                                   (cadr (fq:loc (cadr r))))
                                                fl2)))
                       *FQ-ODF-MARG*))
            ;; Oznaka i model idu do *FQ-ODF-TIT1* + pola visine slova
            ;; iznad rama, pa prozor mora toliko da dosegne; 9.0 je ta
            ;; mera sa zalihom.  Kad iznad stoji modul istog razdelnika,
            ;; naslova nema pa se prozor skuplja na samu ivicu.
            ;;
            ;; Inace se skracuje do ispod prvog tudjeg kucista iznad
            ;; nas.  To sme da ostavi stari naslov nedirnut, pa se javlja
            ;; — bolje vidljiv dupli natpis nego nestalo tudje kuciste.
            vg    (if iznad nil (fq:kucgore svi ul ur vt))
            gore  (cond (iznad 0.15)
                        ((not vg) 9.0)
                        (T (max 0.15 (min 9.0 (- vg vt 0.15))))))
      (if (and vg (< gore *FQ-ODF-TIT1*))
        (princ (fq:tf "kuc.naslov.ostaje" (list oz))))
      (setq br (fq:kucbrisi ul ur vt vb gore 0.15))
      (if (/= (car br) (cdr br))
        (fq:kucodbij oz br)
        (progn
          (fq:odfmod tip oz mod lm kap kn kon st
                     (- ur *FQ-ODF-DX*) ul (- ur *FQ-ODF-DESNO*) ur
                     vtn vbn hm fl2 (not iznad))
          T)))
    (progn
      ;; ODO: naslov do 3.7 iznad rama, nista ispod
      ;; ZOK: naslov do 4.4 iznad, montaza do 1.9 ispod
      ;;
      ;; Mere i skracivanje po susedima racuna fq:kucproz.  Do 23.08.2026
      ;; su tu stajala dva zakucana broja bez ijedne provere suseda, pa je
      ;; tudje kuciste u tom pojasu nestajalo sa svojim FIBERQ zapisom --
      ;; isto ono od cega ODF granu cuva fq:kucgore.  Ista dva broja stoje
      ;; i u c:FQSPLKUC, pa su otisla u zajednicku funkciju: par prepisan
      ;; na dva mesta se razidje tiho.
      (setq pz (fq:kucproz crt oz ul ur vt vb)
            br (fq:kucbrisi ul ur vt vb (car pz) (cadr pz)))
      (if (/= (car br) (cdr br))
        (fq:kucodbij oz br)
        (progn
          (fq:kucdraw tip oz mod kap 1 kn kon mnt lm adr nil st fl2 ang)
          T)))))

;; Redni broj PRVOG vlakna koje lezi unutar rama, gledano odozgo.
;; fl je uredjen odozgo nanize (fq:redovi smanjuje v), pa je prvo koje
;; upadne u raspon vb..vt ujedno i najvise.
;;
;; Namerno se ne trazi tacna visina "vt minus margina": drugi i dalji
;; modul ODF-a imaju uzu marginu od prvog, pa bi takva provera bas na
;; njima promasila.
(defun fq:vlnav (fl vt vb / i r v)
  (setq i 1 r nil)
  (foreach rec fl
    (setq v (cadr (fq:loc (cadr rec))))
    (if (and (not r) (<= v vt) (>= v vb)) (setq r i))
    (setq i (1+ i)))
  r)

;; Svi ramovi kucista u crtezu, izmereni u TEKUCEM (u,v) sistemu.
;;
;; JEDAN prolaz kroz "_X" za tri pitanja koja fq:kucprecrtaj postavlja:
;; ima li iznad modula istog razdelnika, ima li ga ispod, i dokle prozor
;; za brisanje sme navise a da ne pojede tudje kuciste.  Ranije su prva
;; dva pitanja radila po jedan ssget "_C" svako, i to unutar petlje
;; fq:kucsiriend, pored prolaza koje vec rade fq:kucbrisi i fq:kucstari.
;;
;; "_C" je tamo bio i pogresan, ne samo skup: prozorski izbor uzima samo
;; ono sto je u tekucem POGLEDU, pa modul iznad koji nije na ekranu nije
;; bio nadjen.  Tada iznad ispadne nil, ram se produzi navise preko
;; suseda, a prozor za brisanje skoci sa 0.15 na 9.0 i pojede mu ceo
;; crtez.  Ne vidi se dok se ne odzumira, a automatsko sirenje to okida
;; bez ijednog klika.  Isti razlog je zapisan uz fq:kucbrisi i uz
;; fq:snapmodul u fq-sema.lsp.
;;
;; SPLITER je u spisku vrsta iako nije kuciste: njegovo telo lezi na
;; FQ_SPLITER, dakle unutar *FQ-KUC-LEJERI*, pa ga prozor za brisanje
;; obuhvata isto kao i svaki ram.
;;
;; Ugao sme da bude nil — fq:kucugao ga vraca za polilinije sa manje od
;; dva razlicita temena.  Zapis svejedno ulazi, jer za merenje prozora
;; ugao ne treba; onaj ko poredi uglove mora sam da proveri.
;;
;; Spisak se NE sme podici izvan petlje po modulima u fq:kucsiriend.
;; Svako precrtavanje obrise stari ram i napravi nov entitet na skoro
;; istom mestu, pa bi drugi modul merio po ivicama koje vise ne postoje.
;; Zato po modulu ide jedan prolaz — i dalje upola manje nego pre, kada
;; su fq:odfiznad i fq:odfispod radile po jedan svaka.
;;
;; Zapis: (vrsta oznaka ugao ul ur vt vb).
;; OSMI clan je ogledalo, dopisan na kraj.  fq:kucgore cita 3, 4 i 6, a
;; fq:odfsused 0 do 6, pa se raspored ne sme pomeriti.
;;
;; Treci clan je od uvodjenja ogledala ugao KABLA (fq:kucori), ne ugao
;; prve ivice.  Bez toga bi ogledalan modul slozenog razdelnika vratio
;; ang+180, fq:odfsused ga ne bi prepoznao kao suseda, i prozor za
;; brisanje bi skocio sa 0.15 na 9.0 -- pojeo bi modul iznad, sa njegovim
;; FIBERQ zapisom.  To je isti kvar koji je vec jednom bio tu, opisan uz
;; fq:kucgore, samo bi ga sada okidalo ogledalo.
(defun fq:kucsvi ( / ss i e dl d pts uu vv tab out ori)
  (setq ss  (fq:ssx (list '(0 . "LWPOLYLINE")
                          (cons 8 *FQ-KUC-LEJERI*)
                          '(-3 ("FIBERQ"))))
        tab (fq:snapprostor)
        i   0
        out nil)
  (if ss
    (while (< i (sslength ss))
      (setq e  (ssname ss i)
            dl (entget e)
            d  (fq:xdget e))
      (if (and d
               (member (car d) '("ODF" "PP" "ODO" "IPAN" "ZOK" "SPLITER"))
               ;; ne diraj drugi list, "_X" prolazi kroz sve
               (or (not (assoc 410 dl))
                   (not tab)
                   (= (cdr (assoc 410 dl)) tab))
               (>= (length (setq pts (fq:pl10 e))) 4))
        (setq ori (fq:kucori e)
              pts (mapcar 'fq:loc pts)
              uu  (mapcar 'car  pts)
              vv  (mapcar 'cadr pts)
              out (cons (list (car d) (fq:f 1 d)
                              (if ori (car ori) nil)
                              (apply 'min uu) (apply 'max uu)
                              (apply 'max vv) (apply 'min vv)
                              (if ori (cadr ori) nil))
                        out)))
      (setq i (1+ i))))
  out)

;; Ram ISTOG razdelnika koji dodiruje ivicu v u smeru smer (1 navise,
;; -1 nanize).  Sluzi za dve stvari kod slozenog ODF-a: modul iznad
;; znaci da su oznaka i model vec ispisani iznad NJEGA pa se ne
;; ponavljaju, a modul ispod znaci da ram ne sme da se spusti preko
;; njega.  Vraca ceo zapis suseda, ili nil.
;;
;; Vraca zapis a ne T, da bi sledeca izmena imala odakle da uzme meru.
;; Namerno NE vraca golu koordinatu: 0.0 je u AutoLISP-u istinito, pa bi
;; svaki `(if iznad ...)` i dalje radio, ali bi citalac koji to ne zna
;; napisao `(if (> iznad 0) ...)` i tiho ukinuo granu.
;;
;; "Isti razdelnik" je oznaka iz xdata polja 1 PLUS sirina PLUS ugao.
;; Sirina sama je slab kriterijum: ona je samo posledica duzine oznake
;; modula (fq:odfsir), pa dva razdelnika sa jednako dugim oznakama
;; ispadnu jednako siroki.  Oznaka sama takodje nije dovoljna, jer sme
;; da bude "" (fq:odfmod, grana `naslov`).  Zato oba, i uz njih ugao:
;; ram se cita u tekucem *fq-a*, pa zarotiran sused daje tudju meru.
;;
;; Zazor ide do *FQ-PITCH* + *FQ-GAP*, jer kad granica modula padne na
;; prelaz cevcice ramovi se ne dodiruju nego izmedju njih ostane razmak.
;; Nas sopstveni ram je takodje u spisku — njega odbacuje bas ovaj
;; uslov, jer mu je nasuprotna ivica citava visina rama daleko.
(defun fq:odfsused (svi oz ang ul ur v smer / pojas r x xv)
  (setq pojas (+ *FQ-PITCH* *FQ-GAP* 0.2)
        r     nil)
  (foreach x svi
    ;; ivica suseda okrenuta ka nama: kad gledamo navise, to je njegova donja
    (setq xv (if (> smer 0) (nth 6 x) (nth 5 x)))
    (if (and (member (car x) '("ODF" "PP"))
             (= (nth 1 x) oz)
             ang
             (nth 2 x)
             (= (nth 2 x) ang)
             ;; Sused mora da bude i na ISTU stranu okrenut.  Ugao je od
             ;; uvodjenja ogledala ugao kabla, pa ga sam vise ne
             ;; razlikuje: ogledalan i obican modul istog razdelnika bi
             ;; se poklopili po uglu, po oznaci i po sirini, a to su dva
             ;; razlicita rasporeda.  Tekuce ogledalo je vec u *fq-m*,
             ;; postavio ga je fq:kucspoji pre precrtavanja.
             (eq (not (nth 7 x)) (not (< *fq-m* 0.0)))
             (< (abs (- (nth 3 x) ul)) 0.05)
             (< (abs (- (nth 4 x) ur)) 0.05)
             (> (* smer (- xv v)) -0.05)
             (< (* smer (- xv v)) pojas))
      (setq r x)))
  r)

(defun fq:odfiznad (svi oz ang ul ur vt) (fq:odfsused svi oz ang ul ur vt  1))
(defun fq:odfispod (svi oz ang ul ur vb) (fq:odfsused svi oz ang ul ur vb -1))

;; Ivica najblizeg kucista u zadatom smeru, ili nil.  Za smer 1 to je
;; DONJA ivica najnizeg kucista IZNAD nas, za smer -1 GORNJA ivica
;; najviseg ISPOD nas.
;;
;; Jedno telo za oba smera, isti oblik i isti razlog kao fq:odfsused
;; dvadesetak redova iznad.  Prepisano telo za nanize razlikovalo bi se
;; od ovoga u jednom indeksu i dva znaka poredjenja -- razlika koja se u
;; citanju ne primeti, a vidi se tek kad tudje kuciste nestane iz crteza.
;;
;; Prozor za brisanje ide do 9.0 iznad ODF rama -- 4.5 iznad ODO, 5.0
;; iznad kutije i 2.4 ispod nje -- jer toliko treba da bi
;; obuhvatio oznaku i model.  To je vise nego sto ijedno kuciste zauzima
;; po visini, pa tudji ODO ili telo splitera postavljeni tri jedinice
;; iznad ODF modula upadnu ceo u prozor i nestanu — sa svojim FIBERQ
;; zapisom, dakle i iz specifikacije i iz budzeta.  Nijedna provera to ne
;; hvata: `iznad` je za njih TACNO nil, jer to i jesu razliciti elementi.
;;
;; Vodoravni uslov je isti onaj koji fq:kucbrisi stvarno primenjuje —
;; entitet mora da bude CEO unutar ul-0.6 .. ur+0.6.  Kuciste koje tu
;; granicu prelazi prozor ionako ne bi obrisao, pa nema razloga da se
;; zbog njega prozor skracuje.  Time obe funkcije mere po istom merilu.
;;
;; Prag je vt MINUS 0.05, ne plus.  Sused ciji ram lezi TACNO na nasoj
;; gornjoj ivici — a to je najcesci raspored, tako se moduli i crtaju —
;; sa plusom bi ispao ispod praga i prozor bi ostao 9.0.  Kod modula
;; istog razdelnika to ne bi smetalo (`iznad` ih hvata, pa se dovde i ne
;; stize), ali kod TUDJEG razdelnika iste sirine bi ram nizi od 9.0 ceo
;; upao u prozor i nestao.
;;
;; Nas sopstveni ram ovaj prag ne moze da prodje: njegova donja ivica je
;; citava visina rama ispod gornje, a i najplici modul je visok 0.8.
(defun fq:kucsused (svi ul ur v smer / u1 u2 r x xv)
  (setq u1 (- ul 0.6)
        u2 (+ ur 0.6)
        r  nil)
  (foreach x svi
    ;; ivica suseda okrenuta ka nama: kad gledamo navise to je njegova
    ;; donja (nth 6), kad gledamo nanize njegova gornja (nth 5)
    (setq xv (if (> smer 0) (nth 6 x) (nth 5 x)))
    (if (and (>= (nth 3 x) u1)
             (<= (nth 4 x) u2)
             ;; iza nase ivice, sa istim zazorom od 0.05 na obe strane;
             ;; za smer 1 je to doslovno stari uslov (> xv (- vt 0.05))
             (> (* smer (- xv v)) -0.05)
             ;; najblizi, ne prvi nadjeni
             (or (not r) (< (* smer xv) (* smer r))))
      (setq r xv)))
  r)

(defun fq:kucgore (svi ul ur vt) (fq:kucsused svi ul ur vt  1))
(defun fq:kucdole (svi ul ur vb) (fq:kucsused svi ul ur vb -1))

;; Prozor za brisanje ODO ormara i ZOK kutije, skracen po susedima.
;; Vraca (gore dole), spremno za fq:kucbrisi.
;;
;; Do 23.08.2026 su te dve mere bile zakucane, i to na DVA mesta --
;; ovde u fq:kucprecrtaj i u c:FQSPLKUC -- bez ijedne provere suseda.
;; ODF grana odavno pita fq:kucgore dokle prozor sme; ODO i ZOK nisu
;; pitali nista, pa je tudji spliter ili nisko kuciste dve jedinice
;; iznad rama upadalo CELO u prozor i nestajalo sa svojim FIBERQ
;; zapisom -- dakle i iz specifikacije i iz budzeta, bez ijedne poruke.
;; Isti razred kvara i nanize, gde ZOK ide 2.4 ispod rama zbog natpisa
;; montaze.  Uzeg je dometa nego kod ODF-a (4.5 naspram 9.0), ali je
;; ishod isti: nestao podatak, a ne pomeren crtez.
;;
;; Prozor sme samo da se SKRATI.  Kad suseda nema, mere su doslovno one
;; koje su i pre stajale, pa se zatecen crtez ponasa nepromenjeno.
;;
;; Donja granica je 0.15 na obe strane, ista kao u ODF grani: nas ram
;; MORA da stane u prozor, inace ga fq:kucbrisi ne obrise, fq:kucodbij
;; odustane i kuciste se ne precrta uopste.
;;
;; svi se cita ovde, a ne prima kao argument, i razlog je prozaican:
;; dva pozivaoca, a nijedan ga nema pri ruci.  fq:kucprecrtaj ga za ODF
;; granu vec ima, ali ga ODO/ZOK grana ne racuna; c:FQSPLKUC ga nema
;; uopste.  Predaja argumentom bi bila bezbedna -- unutar jednog poziva
;; jos nista nije obrisano -- samo bi je oba pozivaoca morala da racunaju.
;;
;; Ono sto se NE sme je podici fq:kucsvi izvan petlje po modulima u
;; fq:kucsiriend: tamo svaki korak obrise stari ram i napravi nov, pa
;; spisak stariji od jednog PRECRTAVANJA meri po ivicama kojih vise nema.
;; To upozorenje stoji uz fq:kucsvi i vazi tamo, ne ovde.
;;
;; Ostaje jedno: fq:kucsvi zna samo za RAMOVE, pa tudji natpis koji visi
;; izvan svog rama i dalje moze da nestane.  To je svesno isto kao u ODF
;; grani -- izgubljen natpis je kozmetika, izgubljen ram sa xdata je
;; podatak.
(defun fq:kucproz (crt oz ul ur vt vb / svi zg zd ng nd vg vd gore dole)
  (setq svi  (fq:kucsvi)
        ;; Zeljeni prozor: koliko crtac stvarno ispisuje izvan rama.
        ;; ODO drzi model i montazu UNUTAR rama, pa mu ispod treba samo
        ;; debljina linije; ZOK montazu ispisuje ISPOD (fq:zokram).
        ;;
        ;; cond po vrsti, ne (if (= crt "odo") ... ...): drugi oblik znaci
        ;; "sve sto nije ormar dobija kutijine brojeve", pa bi treci crtac
        ;; iz korisnickog kataloga tiho nasledio prozor od 2.4 ispod rama
        ;; a da ispod njega nema sta da stoji.  Nepoznata vrsta zato dobija
        ;; NAJUZI prozor: nas ram nestane, tudje ne moze -- najgore sto se
        ;; desi je udvojen natpis, a to se vidi.
        ;; ZOK je od 24.08.2026 sa 5.0 na 7.0: iznad oznake stoji jos
        ;; jedan red, adresa na (* 4.2 *FQ-ZOK-TITH*) = 5.88.  Prozor
        ;; koji je ne dosegne ostavlja staru adresu u crtezu pri svakom
        ;; precrtavanju, pa bi se posle FQDODAJ-a videle dve.  Susedno
        ;; kuciste je i dalje zasticeno: gore se skracuje na vg.
        zg   (cond ((= crt "odo") 4.5) ((= crt "zok") 7.0) (T 0.15))
        zd   (cond ((= crt "odo") 0.15) ((= crt "zok") 2.4) (T 0.15))
        ;; Prag ispod koga natpis ostaje u crtezu.  fq:kucbrisi meri TEXT
        ;; po tacki umetanja -- fq:etacke za njega vraca samo nju -- pa je
        ;; prag bas visina na koju ga crtac zakaci, ne gornja ivica slova.
        ;; Isto merilo koje ODF grana ima u *FQ-ODF-TIT1*.  Kod ODO su to
        ;; DVA reda i uzima se VISI: prozor raste navise, pa onaj koji
        ;; dosegne adresu (*FQ-ODO-TIT2* = 1.50) jos uvek ne dosegne
        ;; oznaku (3.10) -- natpis bi ostao a poruke ne bi bilo.
        ;; Uzima se NAJVISI natpis: prozor raste navise, pa onaj koji
        ;; dosegne nizi natpis jos uvek ne dosegne visi -- natpis bi
        ;; ostao a poruke ne bi bilo.  Broj dolazi iz fq:kucgornji, koju
        ;; cita i pojas pomeranja u fq:kuctekst; dve kopije istog broja
        ;; su se vec jednom razisle.
        ;;
        ;; ODF ostaje na 0.15 i NE ide kroz fq:kucgornji: njegov prozor
        ;; racuna zaseban put iznad, a ovde bi 6.30 promenilo ponasanje
        ;; koje sa adresom nema veze.
        ng   (cond ((member crt '("odo" "zok")) (fq:kucgornji crt))
                   (T 0.15))
        vg   (fq:kucgore svi ul ur vt)
        ;; Ispod ODO rama nema niceg, pa se sused nanize i ne trazi.
        ;; Njegov prozor je 0.15, dakle vec najuzi moguc: sta god
        ;; fq:kucdole vrati, (max 0.15 (min 0.15 X)) je opet 0.15.  Prolaz
        ;; kroz ceo spisak ramova cija se vrednost ne moze upotrebiti nije
        ;; provera nego trosak koji lici na proveru.
        vd   (if (= crt "odo") nil (fq:kucdole svi ul ur vb))
        nd   *FQ-ZOK-TITH*
        ;; min zeljeno = nikad duze nego pre; max 0.15 = nikad krace od
        ;; sopstvenog rama.  Doslovno obrazac koji ODF grana vec koristi.
        gore (if vg (max 0.15 (min zg (- vg vt 0.15))) zg)
        dole (if vd (max 0.15 (min zd (- vb vd 0.15))) zd))
  ;; Skracen prozor sme da ostavi stari natpis, pa se to JAVLJA -- bolje
  ;; vidljiv dupli natpis nego nestalo tudje kuciste.  Ista razmena i
  ;; ista poruka kao u ODF grani.
  (if (and vg (< gore ng)) (princ (fq:tf "kuc.naslov.ostaje"  (list oz))))
  (if (and vd (< dole nd)) (princ (fq:tf "kuc.montaza.ostaje" (list oz))))
  (list gore dole))

;; Precrtavanje odbijeno: stari crtez nije mogao ceo da nestane.
;;
;; princ, a NE fq:kucreci: fq:kucreci namerno cuti u automatskom
;; sirenju, ali se ovo bas tamo okida, bez ijednog klika na kuciste.
;; Posledica cutanja bi bila dva rama jedan preko drugog, oba sa svojim
;; FIBERQ zapisom — specifikacija ih broji dvaput, a na papiru se ne
;; razlikuju od jednog.
(defun fq:kucodbij (oz br)
  (princ (fq:tf "kuc.nije.obrisan" (list oz (itoa (cdr br)) (itoa (car br)))))
  nil)

;;; --- sirenje rama BEZ precrtavanja ---------------------------------
;;;
;;;  Kuciste sa spliterom se ne moze precrtati: telo, lepeza i ulaz se iz
;;;  spiska vlakana ne rekonstruisu, pa bi ih fq:kucbrisi obrisao bez
;;;  povratka.  Do sada je zato snop veci od rama prosto VIRIO ispod
;;;  njega -- a od 22.08.2026 je to najcesci slucaj, jer dugmad
;;;  kapaciteta polazu ormar SA spliterom.
;;;
;;;  Zato slabiji zahvat: ram se samo RASTEGNE preko snopa.  Portovi,
;;;  telo i lepeza ostaju gde jesu, i to je tacno -- oni stoje uz svoje
;;;  konektore, a konektori se ne pomeraju.  Vlakna koja su tako usla u
;;;  ram a nemaju port su rezerva; ram sada bar pokazuje da su unutra.
;;;
;;;  Isti dogovor koji fq:nastsiri ima za nastavak: nikad se ne skuplja,
;;;  i natpisi se pomeraju zajedno sa ivicom za koju su zakaceni.

;; Lejer rama po crtacu.  MDU crta na FQ_ODO, kao i ODO.
(defun fq:kuclej (crt)
  (cond ((= crt "odf") "FQ_ODF")
        ((= crt "odo") "FQ_ODO")
        (T             "FQ_ZOK")))

;; Natpisi zakaceni za gornju odnosno donju ivicu rama.
;;
;; Ne trazi se tacan pomeraj nego POJAS, jer ga svaki crtac meri
;; drugacije: ODO drzi model i montazu UNUTAR rama, tik uz donju ivicu
;; (vb+1.15 i vb+0.55), a ZOK montazu ISPOD njega.  Pojas odozdo ide do
;; vb+1.6, a najnizi red porta lezi na vb + margina = vb+2.0, pa se ta
;; dva ne mogu pomesati.
;;
;; Brojevi portova namerno ostaju: oni stoje uz svoje konektore.
;;;  NAJVISI NATPIS IZNAD RAMA, po crtacu.
;;;
;;;  Jedno mesto, jer ga citaju DVA posla koja moraju da se slazu:
;;;  prozor za brisanje (fq:kucproz, prag ng) i pojas u kome fq:kuctekst
;;;  pomera natpise pri rastezanju rama.  Dok je broj stajao zakucan na
;;;  oba mesta, razisao se tiho -- vidi odeljak ispod.
;;;
;;;  Mera je TACKA UMETANJA natpisa, ne gornja ivica slova: i fq:kucbrisi
;;;  i fq:kuctekst mere TEXT po njoj, kroz fq:txtpt.
(defun fq:kucgornji (crt)
  (cond ((= crt "odf") *FQ-ODF-TIT1*)
        ((= crt "odo") *FQ-ODO-TIT1*)
        ((= crt "zok") (* 4.2 *FQ-ZOK-TITH*))
        (T 0.15)))

;;;  POJAS JE BIO ZAKUCAN NA 5.0 I NIJE POKRIVAO SVE CRTACE.
;;;
;;;  ODF nosi oznaku na *FQ-ODF-TIT1* = 6.30, dakle IZNAD pojasa: pri
;;;  rastezanju rama pomerali su se model i pregrada, a oznaka je
;;;  ostajala gde je bila.  To je zatecen kvar, stariji od adrese na
;;;  kutiji, i vidi se tek kad ram poraste za vise od pola visine slova.
;;;
;;;  Adresa ZOK kutije (4.2 * *FQ-ZOK-TITH* = 5.88) je upala u istu rupu
;;;  cim je uvedena: razmak do oznake je 2.24, pa bi vec rastezanje za
;;;  0.84 dalo dva natpisa jedan preko drugog, a za 2.24 bi adresa
;;;  zavrsila ISPOD oznake.
;;;
;;;  Zato pojas ide iz fq:kucgornji, uz 0.6 rezerve.  Za ODO se time
;;;  SUZAVA sa 5.0 na 3.70 -- i to je ispravno: ormar iznad 3.10 nema
;;;  nijedan natpis, a uzi pojas je manja opasnost za suseda.
(defun fq:kuctekst (lay crt ul ur vt vb dvt dvb / ss i e lc n gore)
  (setq ss   (fq:ssx (list '(0 . "TEXT") (cons 8 lay)))
        i    0
        n    0
        gore (+ (fq:kucgornji crt) 0.6))
  (if ss
    (while (< i (sslength ss))
      (setq e  (ssname ss i)
            lc (fq:loc (fq:txtpt (entget e))))
      (if (and (> (car lc) (- ul 0.6)) (< (car lc) (+ ur 0.6)))
        (cond
          ((and (> (cadr lc) (+ vt 0.05)) (< (cadr lc) (+ vt gore)))
             (fq:txtpom e 0.0 dvt) (setq n (1+ n)))
          ;; Donja granica je ISPOD dna rama, kao sto je gornja iznad
          ;; vrha.  Do 24.08.2026 je glasila (+ vb 1.6) i zalazila 1.6
          ;; UNUTAR kutije -- a najnizi broj porta stoji na
          ;; (+ vb *FQ-ZOK-MARG*), dakle 1.50.  Rastezanje rama je zato
          ;; broj 12 odvlacilo na dno, dok su 1..11 ostajali gore.
          ;;
          ;; 1.6 je ODF-ova margina, i tamo je uslov prolazio na dlaku
          ;; (1.60 nije < 1.60).  Traka je ocito merena po ODF-u pa
          ;; nikad proverena po ZOK-u, koji ima manju marginu.
          ;;
          ;; Ovaj pojas sluzi TACNO jednom natpisu u celom projektu --
          ;; ZOK montazi na (- vb (* 1.0 *FQ-ZOK-TITH*)) = vb - 1.40.
          ;; Sve iznad vb je sadrzaj kutije i ne sme se dirati.
          ((and (< (cadr lc) (- vb 0.05)) (> (cadr lc) (- vb 2.5)))
             (fq:txtpom e 0.0 dvb) (setq n (1+ n)))))
      (setq i (1+ i))))
  n)

;; Uspravna pregrada unutar rama.  Trazi se po tome sto ide od ivice do
;; ivice: linija na lejeru rama, oba temena na istom u unutar raspona
;; rama, a po visini tacno vb..vt.  Lepeza splitera taj uslov ne
;; ispunjava -- njena temena nisu na istom u -- pa je ne dira.
(defun fq:kucpregr (lay ul ur vt vb nvt nvb / ss i e d l1 l2 n)
  (setq ss (fq:ssx (list '(0 . "LINE") (cons 8 lay))) i 0 n 0)
  (if ss
    (while (< i (sslength ss))
      (setq e  (ssname ss i)
            d  (entget e)
            l1 (fq:loc (cdr (assoc 10 d)))
            l2 (fq:loc (cdr (assoc 11 d))))
      (if (and (< (abs (- (car l1) (car l2))) 0.01)
               (> (car l1) (- ul 0.01)) (< (car l1) (+ ur 0.01))
               (< (abs (- (max (cadr l1) (cadr l2)) vt)) 0.01)
               (< (abs (- (min (cadr l1) (cadr l2)) vb)) 0.01))
        (progn
          (entmod
            (subst (cons 11 (fq:w (car l2) (if (> (cadr l2) (cadr l1)) nvt nvb)))
                   (assoc 11 d)
                   (subst (cons 10 (fq:w (car l1) (if (> (cadr l1) (cadr l2)) nvt nvb)))
                          (assoc 10 d) d)))
          (setq n (1+ n))))
      (setq i (1+ i))))
  n)

;; Rastegni ram na zadate granice.  Obe smeju da budu nil.
;; Vraca T ako je ram promenjen.
(defun fq:kucramsiri (e crt vtnew vbnew
                      / lay pts uu vv ul ur vt vb nvt nvb novi)
  (setq lay (fq:kuclej crt)
        pts (fq:plpts e)
        uu  (mapcar 'car  pts)
        vv  (mapcar 'cadr pts)
        ul  (apply 'min uu)  ur (apply 'max uu)
        vt  (apply 'max vv)  vb (apply 'min vv)
        nvt (if vtnew (max vt vtnew) vt)
        nvb (if vbnew (min vb vbnew) vb))
  (if (and (< (abs (- nvt vt)) 0.01) (< (abs (- nvb vb)) 0.01))
    nil
    (progn
      ;; Natpisi i pregrada PRE rama: oba se traze po STAROJ ivici, a
      ;; ram je entitet po kome se ta ivica i meri.
      (fq:kuctekst lay crt ul ur vt vb (- nvt vt) (- nvb vb))
      (fq:kucpregr lay ul ur vt vb nvt nvb)
      (setq novi (mapcar
                   '(lambda (p)
                      (list (car p)
                            (cond ((< (abs (- (cadr p) vb)) 0.01) nvb)
                                  ((< (abs (- (cadr p) vt)) 0.01) nvt)
                                  (T (cadr p)))))
                   pts))
      (fq:plsetpts e novi)
      T)))

;; Pomeraj kolone konektora od desne ivice rama.
(defun fq:kucdx (crt)
  (cond ((= crt "odf") *FQ-ODF-DX*)
        ((= crt "odo") *FQ-ODO-DX*)
        (T             *FQ-ZOK-DX*)))

;; Polja kucista iz xdata zapisa rama, citana tacno onim redom kojim ih
;; crtaci upisuju.  Vraca (tip crt oz mod kap k kon mnt lm adr spl st).
(defun fq:kucpolja (d / tip crt oz mod kap k kon mnt lm adr spl st)
  (setq tip (car d)
        crt (fq:kc-crt tip))
  (cond
    ((= crt "odf")
       (setq oz  (fq:f 1 d)  mod (fq:f 2 d)  kap (atoi (fq:f 3 d))
             k   (atoi (fq:f 4 d))  kon (fq:f 5 d)  lm  (fq:f 6 d)
             st  (fq:f 7 d)  mnt "" adr "" spl nil))
    ((= crt "odo")
       (setq oz  (fq:f 1 d)  kap (atoi (fq:f 2 d))  k (atoi (fq:f 3 d))
             mod (fq:f 4 d)  mnt (fq:f 5 d)  adr (fq:f 6 d)
             st  (fq:f 7 d)  kon *fq-kuc-kon*  lm "" spl nil))
    (T
       (setq oz  (fq:f 1 d)  mod (fq:f 2 d)  kap (atoi (fq:f 3 d))
             k   (atoi (fq:f 4 d))  kon (fq:f 5 d)  mnt (fq:f 6 d)
             spl (fq:f 7 d)  st  (fq:f 8 d)  lm "" adr (fq:f 9 d))
       (setq spl (if (= spl "") nil (atoi (substr spl 3))))))
  (list tip crt oz mod kap k kon mnt lm adr spl st))

;; Geometrija rama u sistemu kabla: (ul ur vt vb u0).
;; u0 je kolona konektora, tacka u kojoj kabl zavrsava.
(defun fq:kucgeo (crt e / pts uu vv ur)
  (setq pts (fq:plpts e)
        uu  (mapcar 'car  pts)
        vv  (mapcar 'cadr pts)
        ur  (apply 'max uu))
  (list (apply 'min uu) ur (apply 'max vv) (apply 'min vv)
        (- ur (fq:kucdx crt))))

;; Poruka koja ima smisla samo kada je korisnik sam pokrenuo komandu.
;; Automatsko sirenje cuti: ono se okida uz svaki nacrtan kabl, pa bi
;; inace ispisivalo razloge i tamo gde kuciste uopste nije u igri.
(defun fq:kucreci (pitaj s) (if pitaj (princ s)))

;;  Zasto FQDODAJ nema sta da poveze.  Dva razlicita razloga, dva
;;  razlicita saveta korisniku.
;;
;;  Do 1.0.0 je jedna niska pokrivala oba: "nema sta jos da se poveze,
;;  ram se samo siri".  Korisnik koji je na ODF 48 doveo kabl od 72
;;  vlakna procitao je to kao kvar, jer poruka ne kaze da je razdelnik
;;  PUN -- a to je jedini podatak iz kog se vidi da treba veci modul.
;;
;;  Broj vlakana koja ostaju u kaseti se NAMERNO ne navodi.  Kod slozenog
;;  razdelnika spisak vlakana ide od ovog modula nanize i obuhvata i ona
;;  koja pripadaju modulima ispod, a kap je PO MODULU -- poruka bi tvrdila
;;  da u kaseti ostaje vise nego sto stvarno ostaje.  Bolje bez broja
;;  nego sa pogresnim.
;;
;;  Kljuc je doslovan u obe grane.  (fq:tf (if ...) ...) bi radio, ali bi
;;  postao racunat kljuc, koji provera nad tabelama niski po sopstvenom
;;  ugovoru ne vidi.
(defun fq:kucnema (oz kap k)
  (if (<= (- kap k) 0)
    (princ (fq:tf "dod.pun"      (list oz (itoa kap))))
    (princ (fq:tf "dod.nema.sta" (list oz)))))

;; Koliko jos vlakana povezati.  Nula je dozvoljena i znaci: samo
;; rasiri ram, vlakna ostaju u kucistu nepovezana.
(defun fq:kucpitaj (nn pod / n)
  (if (not (setq n (getint (fq:tf "dod.pitaj.koliko" (list (itoa pod) (itoa nn))))))
    (setq n pod))
  (max 0 (min n nn)))

;; JEZGRO.  Precrtava jedno kuciste sa novim spiskom vlakana.
;;
;;   e      ram kucista
;;   d      njegov xdata zapis
;;   fl     vlakna kabla, svedena na kraj kod kucista
;;   ang    ugao tog kabla
;;   pitaj  T pita koliko vlakana da poveze i objasnjava zasto odustaje,
;;          nil samo siri ram i cuti
;;
;;  Vraca T ako je kuciste precrtano.
(defun fq:kucspoji (e d fl ang pitaj
                    / p tip crt oz mod kap k kon mnt lm adr spl st
                      g ul ur vt vb u0 unaz stari unija vise novi off
                      fl2 prvo nn kn vs spl2 kang cang ogl ori gr sir nk
                      vlp)
  (setq p   (fq:kucpolja d)
        tip (nth  0 p)  crt (nth  1 p)  oz  (nth  2 p)  mod (nth 3 p)
        kap (nth  4 p)  k   (nth  5 p)  kon (nth  6 p)  mnt (nth 7 p)
        lm  (nth  8 p)  adr (nth  9 p)  spl (nth 10 p)  st  (nth 11 p))
  ;; Ugao rama se PITA, ne pogadja se.  fq:kucugao ga cita iz prve
  ;; ivice polilinije i radi nad svetskim temenima (fq:pl10), pa mu
  ;; tekuci *fq-a* ne treba — zato sme i pre nego sto se *fq-a* postavi.
  ;;
  ;; ang se provlaci kroz fq:uvek pa fq:stepen samo da bi usao u ISTI
  ;; domen u kom fq:kucugao vraca svoj: celi stepeni 0..359.  Bez toga
  ;; bi -90 i 270 bili razliciti brojevi, a to je isti pravac.
  ;; Ogledalo se cita iz RAMA, ne iz snopa, i to je odluka.  Snop bi na
  ;; kraju kabla uvek rekao "ogledalno", pa bi svako automatsko sirenje
  ;; tiho okrenulo zatecen ormar u kome se kabl zavrsava: izgled bi se
  ;; popravio, ali bi se geometrija promenila bez korisnikovog poteza, i
  ;; to na kucistu koje u toj komandi nije ni dotaknuto.  Zatecen crtez
  ;; zato ostaje kakav jeste; okretanje trazi FQOGLEDALO.
  ;; fq:kucori vraca ugao KABLA, pa poredjenje ispod ostaje isto kakvo je
  ;; bilo: ogledalno kuciste uz isti kabl daje istu vrednost, a zarotiran
  ;; ram i dalje ispada.  Ogledalo se ovde samo pretace u *fq-m*, cime
  ;; sve cetiri mere ispod stizu u ispravnom sistemu.
  (setq ori  (fq:kucori e)
        kang (if ori (car  ori) nil)
        ogl  (if ori (cadr ori) nil)
        cang (fq:stepen (fq:uvek ang)))
  (fq:sistem ang ogl)
  (setq g   (fq:kucgeo crt e)
        ul  (nth 0 g)  ur (nth 1 g)  vt (nth 2 g)  vb (nth 3 g)
        u0  (nth 4 g))
  (cond
    ;; Ram bez dva razlicita temena nema pravac, pa se ne moze ni
    ;; procitati ni precrtati.  Isto to, istim recima, javlja fq:porram.
    ((not kang)
       (fq:kucreci pitaj (fq:tf "kuc.ugao.nema" (list oz)))
       nil)
    ;; Ram nije u istoj orijentaciji kao kabl: sve sto sledi citalo bi
    ;; geometriju u pogresnom sistemu.  Zato je ovo PRVA prava grana —
    ;; ispod nje i prozor kojim se trazi spliter meri u sistemu kabla.
    ;;
    ;; Do 1.0.0 se ugao merio POSREDNO: racunala se sirina kakvu bi
    ;; crtac napravio (fq:kucsirina) i poredila sa (- ur ul), uz misao
    ;; da zarotiran ram da svoju visinu pa se neslaganje vidi.  Ta mera
    ;; je imala tri rupe, pa je i uklonjena:
    ;;   1. Za ODF je vracala samo levu pregradu (fq:odfsir), a ram ide
    ;;      preko obe, pa je promasivala za tacno *FQ-ODF-DESNO* = 5.80
    ;;      pri toleranciji 0.5 — ODF i patch panel nisu prolazili
    ;;      NIKADA, ni pod jednim uglom, a poruka je krivila ugao.
    ;;   2. I ispravljena, ostajala je netacna: crtac meri levu pregradu
    ;;      po NAJDUZOJ oznaci u razdelniku (fq:drawodfn), a zapis
    ;;      modula ne nosi ni broj modula ni tudje oznake, pa bi od
    ;;      desetog modula navise ("L1, M10" je za cifru sire od
    ;;      "L1, M1", oko 1.3) isti razdelnik primao klik na M10 a
    ;;      odbijao klik na M1.
    ;;   3. Sirina uopste nije dokaz ugla: ram okrenut za 180 ima istu
    ;;      sirinu pa je prolazio uvek, a ODO je sirok tacno 12.00 i na
    ;;      kablu od 11 vlakana mu je i visina 12.00, pa je i zarotiran
    ;;      prolazio i bivao precrtan naopako, tiho.
    ;; Ugao nema nijednu od te tri rupe: fq:Wrect pravi temena redom
    ;; (ul vt) (ur vt) (ur vb) (ul vb), pa prva ivica pokazuje tacno u
    ;; smeru +u kucista — u svakom ramu koji je FiberQ ikada nacrtao.
    ;; Zapis kucista ugao ne nosi, ali ga CRTEZ nosi; na toj istoj ivici
    ;; vec stoje fq:kucsvi, fq:porram, c:FQSPLKUC i c:FQIZVOD.
    ((/= kang cang)
       (fq:kucreci pitaj
         (fq:tf "kuc.ugao.razlika" (list oz (itoa kang) (itoa cang))))
       nil)
    ;; Kuciste sa spliterom: izlazi su vec svi nacrtani, a telo,
    ;; lepeza i ulaz se ne mogu rekonstruisati iz spiska vlakana, pa bi
    ;; ih precrtavanje obrisalo bez povratka.
    ;;
    ;; Trazi se TEK ovde: fq:splukut gleda da li spliter upada u
    ;; pravougaonik ul-ur, vt-vb, a te cetiri mere vrede samo kad je
    ;; ugao potvrdjen.
    ((setq spl2 (if spl
                  (strcat oz "-SP 1:" (itoa spl))
                  (fq:splukut ul ur vt vb)))
       ;; Precrtati se ne moze, ali RASTEGNUTI se moze, a portovi smeju
       ;; da se DOPISU.  Oboje su izmene koje nista ne brise, pa telo,
       ;; lepeza i ulaz ostaju netaknuti.
       ;;
       ;; Meri se ceo fl, ne fl2: fl2 se racuna tek nize, u grani koja
       ;; precrtava, a ovde je pitanje prosto koliko je kabl visok.
       ;; fq:kucnakraju je vec potvrdio da kabl bas ovde i zavrsava.
       (setq vs  (mapcar '(lambda (r) (cadr (fq:loc (cadr r)))) fl)
             gr  (fq:kucgran crt oz ang ul ur vt vb vs)
             sir (fq:kucramsiri e crt (car gr) (cadr gr))
             ;; Portovi se dopisuju TEK posle rastezanja: fq:splkon
             ;; uzima samo vlakna koja leze unutar rama, a bas ona su do
             ;; malopre visila ispod njega.
             ;;
             ;; Samo kad je korisnik sam pokrenuo komandu.  Automatsko
             ;; sirenje se okida uz svaki nacrtan kabl i nema od koga
             ;; da pita koliko vlakana ide na portove; ram rastegne i
             ;; cuti, kao i do sada.
             ;;
             ;; Samo ormar.  ZOK kutija ima SVE konektore fizicki
             ;; nacrtane od prvog crtanja, pa slobodnog reda ispod snopa
             ;; u njoj nema; razdelniku brojevi idu unapred pa bi
             ;; dopisan port pao na izlaz splitera.
             nk  (if (and pitaj (= crt "odo"))
                   (fq:splkon oz kap ul ur (car gr) (cadr gr) u0 fl pitaj)))
       (cond
         (nk
            ;; Polje 3 zapisa ormara je UKUPAN broj zauzetih portova,
            ;; vlakna i izlazi zajedno (fq:drawodospl upisuje ktot).
            ;; Bez ovoga bi specifikacija i dalje tvrdila da je ormar
            ;; prazniji nego sto jeste.
            (fq:xdput e 3 (itoa (+ k (car nk))))
            (princ (fq:tf "spl.dopisano"
                          (list oz spl2 (itoa (car nk))
                                (itoa (cadr nk)) (itoa (caddr nk))
                                (itoa (+ k (car nk))) (itoa kap))))
            T)
         (sir
            (princ (fq:tf "kuc.spl.rastegnut" (list oz spl2 (itoa (length fl)))))
            T)
         (T
            (fq:kucreci pitaj (fq:tf "kuc.ima.spliter" (list oz spl2)))
            nil)))
    (T
     (setq ;; ODF broji unapred, ormari i kutije unazad od kapaciteta
           unaz  (/= crt "odf")
           stari (fq:kucstari (fq:kucvez oz lm crt) kap unaz u0)
           ;; Stanje kucista je citljivo iz samih vlakana samo ako se
           ;; broj i redosled poklapaju sa onim sto ram tvrdi.  Kuciste
           ;; postavljeno samostalno nema veza ni na jednom vlaknu, pa
           ;; se za njega ide starim putem.
           ;;
           ;; (> k 0) nije suvisno: prazan modul slozenog ODF-a nosi
           ;; k=0, pa bi bez toga uslov bio 0=0 i prazan modul bi usao
           ;; u uniju bez ijedne procitane veze.
           unija (and (> k 0)
                      (= (length stari) k)
                      (fq:kucredom stari))
           ;; od kog vlakna kabla ovaj ram pocinje: prvo koje upada u
           ;; njegov raspon po visini
           off   (fq:vlnav fl vt vb))
     ;; Crtacu uvek ide PUN spisak vlakana, i onih koja se sada ne
     ;; povezuju.  Ram i raster konektora se racunaju iz te liste, pa bi
     ;; krnja lista skupila ram: ODO koji je obuhvatao ceo snop od 48
     ;; vlakana spao bi na visinu prvih sedam.  Broj povezanih (k) samo
     ;; odredjuje kome se upisuje veza.
     (if unija
       ;; Kabl NE mora da bude onaj na kom kuciste vec stoji: drugi kabl
       ;; se prosto doda na spisak, pa crtac napravi ram koji obuhvata
       ;; oba.  Izmedju vezanih i novih idu ona koja u kucistu vise, da
       ;; im precrtavanje ne pojede kolutove.
       (setq vise (fq:kucvise u0 vt vb (mapcar 'caddr stari))
             novi (vl-remove-if
                    '(lambda (r) (or (fq:imae stari (caddr r))
                                     (fq:imae vise  (caddr r)))) fl)
             fl2  (append stari vise novi)
             ;; Za proveru kraja je dovoljno prvo vlakno koje jos nije
             ;; vezano: ono iz ovog kabla ako ga ima, inace ono koje u
             ;; kucistu vec visi.
             prvo (cond (novi (cadr (car novi)))
                        (vise (cadr (car vise))))
             ;; Povezuju se redom oni koji vise, pa tek onda novi: oni
             ;; su vec u kucistu i lezu na sledecim redovima.
             nn   (min (- kap k) (+ (length vise) (length novi))))
       (setq fl2  (if off (fq:podlista fl off))
             prvo (if off (cadr (nth (1- off) fl)))
             nn   (if off (min (- kap k) (- (length fl) (1- off) k)) 0)))
     (cond
       ((not prvo)
          (fq:kucreci pitaj
            (if unija
              (fq:tf "dod.sva.vezana" (list oz))
              (fq:tf "dod.nijedno"    (list oz))))
          nil)
       ;; Visina sama nije dokaz: oba kraja istog kabla leze na istim
       ;; visinama, pa bi klik na pogresan kraj obrisao kuciste sa
       ;; jednog kraja i precrtao ga na drugom.  Zato se proverava i gde
       ;; vlakno ZAVRSAVA: kolona konektora stoji tacno na kraju kabla.
       ((> (abs (- (car (fq:loc prvo)) u0)) 1.0)
          (fq:kucreci pitaj (fq:tf "dod.ne.zavrsavaju" (list oz)))
          nil)
       (T
        (setq vs (mapcar '(lambda (r) (cadr (fq:loc (cadr r)))) fl2))
        (cond
          ;; Automatika ne PRECRTAVA kuciste cije stanje ne moze da
          ;; procita do kraja.  Ono vec nosi upisane veze, ali se ne
          ;; slazu sa onim sto ram tvrdi, pa bi precrtavanje islo samo
          ;; po ovom kablu: ram bi se skupio i odsekao snop koji je vec
          ;; u njemu.  Za povezivanje ostaje FQDODAJ, gde korisnik sam
          ;; kaze sta ce.
          ;;
          ;; Ali RAM se i tada sme rastegnuti.  Rastezanje nista ne
          ;; brise, pa nema cega da se odsece.  Bez toga je kabl veceg
          ;; kapaciteta od zatecenog ostajao da visi ispod rama, tiho i
          ;; bez ijedne poruke -- a to je bas ono zbog cega se ram i
          ;; siri.
          ((and (not pitaj) stari (not unija))
             (setq gr (fq:kucgran crt oz ang ul ur vt vb vs))
             (if (fq:kucramsiri e crt (car gr) (cadr gr))
               (progn (princ (fq:tf "kuc.samo.ram" (list oz))) T)
               nil))
          ;; Automatsko sirenje ima sta da radi samo ako snop viri iz
          ;; rama, ili ako kuciste nema nijednu upisanu vezu, sto znaci
          ;; da je postavljeno samostalno pre nego sto je kabl uopste
          ;; postojao pa mu portovi jos ni na cemu ne stoje.  Bez ove
          ;; provere bi se svako kuciste precrtavalo uz svaki kabl koji
          ;; u njega ulazi, i onda kada bi ispalo isto ovakvo kakvo je.
          ((and (not pitaj)
                stari
                (>= (apply 'min vs) vb)
                (<= (apply 'max vs) vt))
             nil)
          ;;  ISTA ZASTITA I ZA FQDODAJ.  Uslov iznad pocinje sa
          ;;  (not pitaj), pa je vazio samo za automatsko sirenje —
          ;;  kroz FQDODAJ se kuciste brisalo i ponovo crtalo i onda kad
          ;;  nema sta da se poveze A snop vec ceo stoji unutar rama.
          ;;
          ;;  To nije bilo bezopasno.  Poruka je posle toga tvrdila
          ;;  "ram rasiren po snopu od 72 vlakana", a ram se nije ni
          ;;  pomerio; rukohvati kucista su se menjali bez razloga; a na
          ;;  ZAKLJUCANOM lejeru fq:kucbrisi obrise sta stigne pre nego
          ;;  sto fq:kucodbij prijavi da nov crtez nije nacrtan — dakle
          ;;  komanda bez ijednog posla ume da ostavi polovicno kuciste.
          ;;
          ;;  stari u uslovu je obavezan: samostalno postavljeno kuciste
          ;;  nema nijednu upisanu vezu i mora da se precrta i kad je
          ;;  snop unutar rama, jer mu portovi jos ni na cemu ne stoje.
          ((and pitaj (< nn 1) stari
                (>= (apply 'min vs) vb)
                (<= (apply 'max vs) vt))
             (fq:kucnema oz kap k)
             nil)
          (T
           (if (and pitaj (< nn 1)) (fq:kucnema oz kap k))
           ;; kn je NOV broj vlakana na konektorima
           (setq kn
             (cond
               ;; FQDODAJ pita koliko JOS vlakana da se poveze
               ((and pitaj (> nn 0))
                  (+ k (fq:kucpitaj nn (if unija nn 1))))
               (pitaj k)
               ;; Automatika, a kuciste jos nijednom vlaknu nije upisalo
               ;; vezu: postavljeno je pre kabla, pa je dosadasnji broj
               ;; bio samo pretpostavka iz podrazumevane vrednosti.  Tek
               ;; sada se zna koliko kabl stvarno donosi, pa broj sme i
               ;; da se smanji, jer nema sta da se raspaja.  Kada veze
               ;; postoje, broj ostaje kakav jeste: veza se vlaknu
               ;; upisuje, a nigde se ne brise.
               ;; Esc na ovo pitanje NE sme da odnese i sirenje rama.
               ;; Pitanje stize samo od sebe, bez ijednog klika, odmah
               ;; po nacrtanom kablu -- to je najcesci Esc u celom
               ;; dodatku i niko ga ne prepoznaje kao svoj (vidi
               ;; fq:kablsiri).  Ram je geometrija i prati kabl; Esc
               ;; znaci samo "ne diraj veze", pa broj ostaje kakav je
               ;; bio, a precrtavanje se svejedno obavi.
               ((not stari)
                  (setq vlp (vl-catch-all-apply
                              'fq:pitajvl
                              (list (min k   (length fl2))
                                    (min kap (length fl2)))))
                  (if (vl-catch-all-error-p vlp)
                    (progn (princ (fq:t "kuc.pitanje.otkazano"))
                           (min k (length fl2)))
                    vlp))
               (T k)))
           ;; Poruka ide SAMO ako je precrtavanje stvarno proslo.  Kad
           ;; fq:kucprecrtaj odustane, ono je vec reklo zasto; ovo bi
           ;; preko toga tvrdilo da su vlakna povezana, a nisu.
           ;; Crtacu se strana NAMECE, ne prepusta mu se da je ponovo
           ;; izvede iz snopa: on bi je izveo iz fl2, a fl2 je za
           ;; zatecen ram na kraju kabla drugacija od onoga sto ram
           ;; tvrdi.  Precrtavanje sme da promeni sirinu i visinu rama,
           ;; nikad njegovu stranu -- brisanje ide po staroj geometriji,
           ;; pa bi se ram nacrtao tamo gde nista nije obrisano.
           (setq *fq-ogl* (if ogl "da" "ne"))
           (if (fq:kucprecrtaj tip crt oz mod kap kn kon mnt lm adr
                               st fl2 ang ul ur vt vb)
             (progn
               (princ (fq:tf "dod.rezultat" (list oz (if (> kn k) (fq:tf "dod.povezano" (list (itoa (- kn k)) (itoa kn) (itoa kap))) (fq:tf "dod.rasireno" (list (itoa (length fl2)) (itoa kn) (itoa kap)))))))
               T)
             ;; Precrtavanje je odustalo i vec je reklo zasto.  Ram
             ;; posle toga stoji na starim merama, pa snop viri ispod
             ;; njega; rastezanje nista ne brise, pa sme i tu gde
             ;; precrtavanje nije smelo.
             ;;
             ;; Rukohvat se proverava, isti razlog kao u fq:kucsiriend:
             ;; fq:kucbrisi je vec obrisao ono sto je mogao, pa je i sam
             ;; ram mozda otisao -- a rastezanje obrisanog rama pada na
             ;; praznoj listi temena.
             (progn
               (setq gr (fq:kucgran crt oz ang ul ur vt vb vs))
               (if (and (entget e) (fq:kucramsiri e crt (car gr) (cadr gr)))
                 (progn (princ (fq:tf "kuc.samo.ram" (list oz))) T)
                 nil))))))))))

;; Ramovi kucista kojima kolona konektora stoji na koordinati ue, a
;; raspon rama po visini obuhvata v.  Vraca listu parova (ram . xdata).
;;
;; Trazi se po celom crtezu, ali samo po lejerima kucista i samo medju
;; FIBERQ zapisima, pa je to i u velikom projektu nekoliko desetina
;; entiteta.
;;
;; VRACA SE LISTA, ali kod slozenog razdelnika u njoj po pravilu stoji
;; SAMO JEDAN MODUL.  Filter je jedna visina v, a moduli se slazu jedan
;; pod drugim bez preklapanja, pa v moze da upadne u raspon tacno jednog
;; od njih.  v je pri tom visina PRVOG vlakna kraja kabla (vidi
;; fq:kucsiriend), dakle najviseg -- tako automatsko sirenje dohvati
;; gornji modul, a donji ostane na starim merama, sa svojim delom snopa
;; ispod sebe.  Za njega ostaje FQDODAJ.
;;
;; Do 24.08.2026 je ovde pisalo da slozen ODF "vrati sve svoje module",
;; i to je bilo netacno.  Zapisano je jer je bas ta recenica razlog zbog
;; kog fq:kucsiriend ima petlju po ramovima; petlja je korisna i ostaje
;; (na istoj tacki sme da stoji vise od jednog kucista), ali ne radi ono
;; sto je komentar obecavao.  Da se dohvate svi moduli, funkcija bi
;; umesto jedne visine morala da primi RASPON snopa -- zaseban posao,
;; jer dira ceo put automatskog sirenja.
;; Kolona se trazi na OBE strane rama, jer kuciste sme da bude ogledalno.
;; Obicno je kolona kucdx od desne ivice, ogledalno isto toliko od leve.
;; Cita se sa *fq-m* = 1.0, u sistemu kabla, pa se ovde ne prevodi nista
;; -- pravu stranu tek kasnije potvrdjuje fq:kucogl, iz smera prve ivice.
;;
;; Oba uslova stoje ravnopravno, bez provere ugla: ram sirine tacno 2*dx
;; bi zadovoljio oba, a to je ram od 4.00 jedinice, uzi od svakog koji
;; crtaci prave.  Kad bi se takav pojavio, fq:kucspoji bi ga svejedno
;; procitao po svom uglu.
(defun fq:kucnakraju (ue v / ss i e d out crt pts uu vv dx)
  (setq ss  (fq:ssx (list (cons 8 *FQ-KUC-LEJERI*) '(-3 ("FIBERQ"))))
        i   0
        out nil)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i)
            d (fq:xdget e))
      (if (and (fq:kucvrsta d)
               (= (cdr (assoc 0 (entget e))) "LWPOLYLINE"))
        (progn
          (setq crt (fq:kc-crt (car d))
                dx  (fq:kucdx crt)
                pts (fq:plpts e)
                uu  (mapcar 'car  pts)
                vv  (mapcar 'cadr pts))
          (if (and (or (< (abs (- (- (apply 'max uu) dx) ue)) 1.0)
                       (< (abs (- (+ (apply 'min uu) dx) ue)) 1.0))
                   (<= v (apply 'max vv))
                   (>= v (apply 'min vv)))
            (setq out (cons (cons e d) out)))))
      (setq i (1+ i))))
  (reverse out))

;; Sirenje kucista posle crtanja kabla.  Zove se iz fq:kablsiri i
;; fq:lomsiri u fq-sema.lsp, odmah po crtanju.
;;
;; krajevi je lista parova (svetska-tacka . ugao): tacka je mesto na
;; kome PRVO vlakno tu zavrsava, ugao je ugao onog dela kabla koji na taj
;; kraj dolazi.  Kod pravog kabla oba kraja imaju isti ugao, kod kabla sa
;; lomovima i kod izvoda nemaju, pa se svaki kraj cita u svom sistemu.
;;
;; Vraca broj precrtanih kucista.
(defun fq:kucsiriend (oz krajevi / naso raw n kr lc u0 v0 ram fl pk)
  (setq naso nil n 0)
  ;; Prvo ramovi, pa tek onda vlakna.  Spisak vlakana kabla se cita
  ;; prolazom kroz sve FIBERQ zapise na lejeru vlakana, sto u velikom
  ;; projektu nije besplatno, a kucista na kraju kabla najcesce nema.
  ;; fq:sistem, ne golo *fq-a*.  Kraj kabla se meri u sistemu KABLA, a
  ;; fq:kucspoji nize ostavlja *fq-m* postavljen na ono kuciste koje je
  ;; upravo precrtao; sledeci prolaz bi taj znak nasledio i trazio kolonu
  ;; na pogresnoj strani.  Isti razlog vazi i za drugu petlju: fl se cita
  ;; jednom, a fq:kucspoji se izmedju dva modula poziva.
  (foreach kr krajevi
    (fq:sistem (cdr kr) nil)
    (setq lc     (fq:loc (car kr))
          u0     (car lc)
          v0     (cadr lc)
          ram    (fq:kucnakraju u0 v0))
    (if ram (setq naso (cons (list (cdr kr) u0 ram) naso))))
  (if (and naso (setq raw (fq:cablefibers oz)))
    (foreach kr (reverse naso)
      (fq:sistem (car kr) nil)
      (setq fl (fq:krajevi raw (cadr kr)))
      (foreach pk (caddr kr)
        ;; Spisak ramova je skupljen PRE petlje, a svaki korak brise i
        ;; ponovo crta svoj ram, pa se pre svakog proverava da rukohvat
        ;; jos vazi.
        ;;
        ;; Do 1.0.0 je ovo bilo neophodno iz gore razloga: prozor za
        ;; brisanje je umeo da pojede ram SUSEDNOG modula, jer je
        ;; fq:odfiznad radila kroz ssget "_C" pa suseda van ekrana nije
        ;; nalazila.  Sada susedni ram prezivi — prozor od 0.15 ne moze
        ;; da ga obuhvati celog — i provera je opet samo pojas.
        (if (and (entget (car pk))
                 (fq:kucspoji (car pk) (cdr pk) fl (car kr) nil))
          (setq n (1+ n))))))
  n)

;; Prav kabl: oba kraja pod istim uglom, drugi je za lg dalje duz kabla.
(defun fq:kucsirikabl (oz p0 lg ang / lc)
  (fq:sistem ang nil)
  (setq lc (fq:loc p0))
  (fq:kucsiriend oz (list (cons p0 ang)
                          (cons (fq:w (+ (car lc) lg) (cadr lc)) ang))))

;;; ------------------------------------------------------------------
;;;  9b. PORAVNANJE PO RAMU
;;;
;;;  Poravnanje se do sada uzimalo samo sa VLAKNA.  Kada se kabl obrise,
;;;  na kucistu vise nema nijednog vlakna, pa poravnanje nema sta da
;;;  uhvati — a bas tada i treba, jer se isti kabl polaze ponovo.
;;;
;;;  Iz rama se citaju ugao, red prvog konektora i kolona, to jest tacno
;;;  ono sto samostalno nacrtan ormar upise u *fq-por*.  Sledeci kabl
;;;  zato krece iz prvog konektora, bez ijedne pokazane tacke.
;;;
;;;  Uzima se samo PRVI red, ne cela mreza: raspored cevcica kabla koji
;;;  je obrisan ram ne pamti, a kabl od prvog konektora sam nastavlja
;;;  svojim rasterom — a to je isti onaj po kome je ram i nacrtan.
;;;
;;;  Nastavak nema kolonu konektora, pa od njega ide samo red; polozaj
;;;  duz kabla se i dalje pokazuje misem.
;;;
;;;  Vraca *fq-por* zapis (prvi-red ugao redovi kolona) ili nil.
;; Vraca (prviRed ugaoKabla nil kolona ogledalo).
;;
;; Peti clan je dopisan na KRAJ, po istom pravilu koje vazi za polja
;; xdata: *fq-por* se puni bas ovom listom, a (cadddr *fq-por*) je uslov
;; na cetiri mesta u fq-sema.lsp.
;;;  Red na koji sledeci kabl treba da legne kod kucista sa spliterom.
;;;
;;;  Lepeza uvek stoji IZNAD napojnog konektora, ali koliko iznad nije
;;;  isto kod ormara i kod kutije, i to je cela poenta ove funkcije:
;;;
;;;    ODO/MDU  izlazi 1..no idu odmah iznad snopa (fq:izlgore), pa je
;;;             napajanje tacno jedan raster ispod najnizeg izlaza.
;;;
;;;    ZOK      ima SVE konektore fizicki nacrtane od prvog crtanja:
;;;             izlazi sede na konektorima 1..no odozgo, napajanje na
;;;             konektoru kap koji je NAJNIZI, a izmedju njih stoji ceo
;;;             blok slobodnih no+1 .. kap-1.  Jedan raster ispod lepeze
;;;             je zato prvi SLOBODAN konektor, ne napajanje: ZOK 24 sa
;;;             1:8 promasuje za 12.00, ZOK 12 sa 1:4 za 5.60.
;;;
;;;  Redni broj najnizeg izlaza dolazi iz njegovog zapisa, ne iz
;;;  prebrojavanja -- vidi fq:izlnaj.
;;;
;;;  Rezultat se kljesti na (vb + margina): pokvarena ili delimicno
;;;  obrisana lepeza sme da da broj koji ispada ispod rama, a poravnanje
;;;  ispod rama je tise pogresno od promasenog reda -- tamo konektora
;;;  nema uopste, a komanda i dalje javlja da kabl krece iz prvog.
(defun fq:splpor (d ul ur vt vb / p crt kap iz v)
  (setq p   (fq:kucpolja d)
        crt (nth 1 p)
        kap (nth 4 p)
        iz  (fq:izlnaj ul ur vt vb))
  (cond
    ;; Lepeze nema.  Kod kutije se to i dalje zna iz zapisa (polje 7),
    ;; pa poravnanje ide na najnizi konektor -- ne na prvi red rama, koji
    ;; je kod nje suprotan kraj, (kap-1) rastera daleko.
    ((not iz)
       (if (and (= crt "zok") (nth 10 p)) (+ vb (fq:kucmarg crt))))
    (T
     (setq v (if (= crt "zok")
               (- (car iz) (* *FQ-PITCH* (max 0 (- kap (cdr iz)))))
               (- (car iz) *FQ-PITCH*)))
     (max v (+ vb (fq:kucmarg crt))))))

(defun fq:porram (e d / crt pts ang ogl ori v0 u0 g iz)
  (setq ori (fq:kucori e)
        ang (if ori (car  ori) nil)
        ogl (if ori (cadr ori) nil))
  (cond
    ((not (or (= (car d) "NASTAVAK") (fq:kucvrsta d)))
     (princ (fq:t "por.nije.ni"))
     (princ (fq:t "por.klikni"))
     nil)
    ((not ang)
     (princ (fq:t "opste.ram.ugao"))
     nil)
    (T
     (fq:sistem ang ogl)
     (setq pts (fq:plpts e))
     (if (< (length pts) 4)
       (progn (princ (fq:t "opste.ram.temena")) nil)
       (progn
         (if (= (car d) "NASTAVAK")
           (setq g  (fq:ramnast e)
                 v0 (- (nth 2 g) *FQ-NAST-MARG*)
                 u0 nil)
           (progn
             (setq crt (fq:kc-crt (car d))
                   g   (fq:kucgeo crt e)
                   v0  (- (nth 2 g) (fq:kucmarg crt))
                   u0  (nth 4 g))
             ;;  Kuciste sa spliterom drzi izlaze IZNAD snopa, pa prvi
             ;;  red rama nije konektor kabla nego izlaz 1.  Kabl koji
             ;;  se na njega poravna mora da legne ISPOD cele lepeze --
             ;;  inace mu vlakna padaju na portove koje spliter vec
             ;;  drzi, sto je tacno onaj kvar zbog kog je napojni port
             ;;  i spusten dole.
             ;;  Koliko ispod lepeze -- racuna fq:splpor, jer ormar i
             ;;  kutija napojni konektor drze na suprotnim krajevima.
             (if (setq iz (fq:splpor d (car g) (cadr g) (nth 2 g) (nth 3 g)))
               (setq v0 iz))))
         (list v0 ang nil u0 ogl))))))

(defun c:FQDODAJ ( / *error* pk e d oz pc)
  (setq *error* fq:err)
  (fq:init)
  (setq pk (fq:kucpick (fq:t "kuc.pitaj.dodaj")))
  (if pk
    (progn
      (setq e  (car pk)
            d  (cdr pk)
            oz (fq:f 1 d))
      (if (not (setq pc (fq:pickcable (fq:tf "kuc.pitaj.vlakno" (list oz)))))
        (princ (fq:t "opste.otkazano"))
        (fq:kucspoji e d (cadr pc) (caddr pc) T))))
  (princ))


;;; ------------------------------------------------------------------
;;;  9c. SPLITER U VEC NACRTAN ORMAR
;;;
;;;  Redosled iz prakse je obrnut od onog koji dijalog nudi: prvo se
;;;  nacrta ODO sa magistralnim kablom, pa se tek posle odluci da u
;;;  njega ide spliter.  Tada se ormar precrtava sa istim kablom i
;;;  istim brojem zauzetih portova, samo mu se ispod snopa dodaju
;;;  portovi izlaza i telo splitera.
;;;
;;;  Isti obrazac kao FQDODAJ: klik na ram, pa klik na vlakno kabla.
;;;  Stari crtez se brise po zatecenim granicama rama, a novi ram
;;;  racuna crtac iz spiska vlakana i izlaza, pa se sam rasiri.
;;;
;;;  Posle ovoga se ormar vise ne precrtava: ni FQDODAJ ni automatsko
;;;  sirenje ne diraju kuciste koje u sebi ima telo splitera, jer se
;;;  ono iz spiska vlakana ne moze rekonstruisati.  Zato spliter ide
;;;  POSLE svih kablova koji u ormar ulaze.
;;; ------------------------------------------------------------------

(defun c:FQSPLKUC ( / *error* pk e d p tip crt oz mod kap k kon mnt lm adr spl st
                      ang ogl ori g ul ur vt vb u0 pc fl pn fdi sp no br sam pz)
  (setq *error* fq:err)
  (fq:init)
  (setq pk (fq:kucpick (fq:t "spl.pitaj.ormar")))
  (if pk
    (progn
      (setq e   (car pk)
            d   (cdr pk)
            p   (fq:kucpolja d)
            tip (nth  0 p)  crt (nth  1 p)  oz  (nth 2 p)  mod (nth 3 p)
            kap (nth  4 p)  k   (nth  5 p)  kon (nth 6 p)  mnt (nth 7 p)
            lm  (nth  8 p)  adr (nth  9 p)  spl (nth 10 p) st  (nth 11 p)
            ;; Ugao KABLA, ne ugao prve ivice: ormar sme da bude
            ;; ogledalan, a poredjenje nize (osp.ugao) ga poredi sa uglom
            ;; kliknutog vlakna.
            ori (fq:kucori e)
            ang (if ori (car  ori) nil)
            ogl (if ori (cadr ori) nil))
      (cond
        ;; Kutija je pustena unutra od 22.08.2026.  fq:drawzok vec zna da
        ;; nacrta kutiju sa spliterom -- to radi dijalog "Sve vrste" --
        ;; pa je nedostajao samo put da se ista stvar uradi NAKNADNO, nad
        ;; kutijom u koju je kabl vec doveden.  Redosled iz prakse je
        ;; upravo taj: prvo kutija sa kablom, pa odluka o spliteru.
        ;;
        ;; Razdelnik i patch panel ostaju napolju namerno: u letvu se
        ;; spliter ne crta.  Nastavak takodje, i to iz jaceg razloga --
        ;; nema kolonu konektora, pa izlazima splitera nema na sta da se
        ;; nakace.  Vidi fq:drawnast.
        ((not (member crt '("odo" "zok")))
           (princ (fq:tf "osp.nije.ormar" (list oz)))
           (princ (fq:t "osp.kutija.savet")))
        ((not ang)
           (princ (fq:t "opste.ram.ugao")))
        (T
         (fq:sistem ang ogl)
         (setq g   (fq:kucgeo crt e)
               ul  (nth 0 g)  ur (nth 1 g)  vt (nth 2 g)  vb (nth 3 g)
               u0  (nth 4 g))
         ;; PRAZNO KUCISTE se prepoznaje, ne pita.
         ;;
         ;; Kuciste postavljeno samostalno nema nijedno vlakno na koloni,
         ;; pa nema ni sta da se klikne -- a bas to je najcesci redosled:
         ;; ormar se polozi rano, sa kablom ili bez njega, a spliter se
         ;; doda kad se odluci.  Do 22.08.2026 je komanda tu bezuslovno
         ;; trazila vlakno kabla, pa se u prazan ormar spliter nije mogao
         ;; dodati nikako -- moralo se brisati i crtati iz pocetka.
         ;;
         ;; Pitaju se OBA izvora, jer nijedan sam nije potpun:
         ;;
         ;;   fq:kucvise  vlakna ciji KRAJ lezi na koloni, unutar visine
         ;;               rama.  Hvata kabl koji je doveden a jos nije
         ;;               povezan -- npr. kad je sirenje rama odbijeno.
         ;;   fq:kucstari vlakna kojima je ovo kuciste UPISANO u vezu.
         ;;               Hvata kabl koji kroz ram samo prolazi i ide
         ;;               dalje; njemu kraj nije na koloni, pa ga prvi
         ;;               test ne vidi, a portove svejedno zauzima.
         ;;
         ;; Oba su vec u upotrebi u fq:kucspoji, i to na istim
         ;; argumentima, pa se ovo mesto sa njim ne moze razici.
         (setq sam (not (or (fq:kucvise u0 vt vb nil)
                            (fq:kucstari (fq:kucvez oz lm crt) kap
                                         (/= crt "odf") u0))))
         (cond
           ((fq:splukut ul ur vt vb)
              (princ (fq:tf "osp.vec.ima" (list oz))))
           ;; Kapacitet mora da primi napajanje i bar jedan izlaz.  Kod
           ;; praznog kucista je to jedini uslov nad brojevima: zauzetih
           ;; portova nema, niti ih mora biti.
           ((< kap 2)
              (princ (fq:tf "osp.pun" (list oz (itoa kap)))))
           ((and (not sam) (< k 1))
              (princ (fq:tf "osp.nema.porta" (list oz)))
              (princ (fq:t "osp.prvo.kabl")))
           ;; Kutija sa spliterom se gradi oko JEDNOG dolaznog vlakna:
           ;; napajanje ide na poslednji konektor, a izlazi iznad njega,
           ;; po celoj visini kutije (fq:drawzok, grana spl).  Kutija
           ;; koja vec ima vise vezanih vlakana bi tim precrtavanjem
           ;; ostala bez konektora ispod napojnog, a vlakna bi visila
           ;; nevezana -- i to bez ijedne poruke, jer geometrija ne bi
           ;; bila sporna.  Zato se odbija ovde, a ne posle.
           ((and (= crt "zok") (not sam) (> k 1))
              (princ (fq:tf "osp.kutija.vise" (list oz (itoa k)))))
           ((and (not sam) (< (- kap k) 1))
              (princ (fq:tf "osp.pun" (list oz (itoa kap)))))
           ((and (not sam)
                 (not (setq pc (fq:pickcable (fq:tf "osp.pitaj.vlakno" (list oz))))))
              (princ (fq:t "opste.otkazano")))
           ((and (not sam) (/= (caddr pc) ang))
              (princ (fq:tf "osp.ugao" (list oz (itoa ang) (itoa (caddr pc))))))
           (T
            (if sam
              ;; Snop se ne klikce nego se REKONSTRUISE iz rama, istim
              ;; putem kojim fq:kucask crta samostalno kuciste sa
              ;; spliterom: fq:virtfl pravi zapise sa rukohvatom nil, a
              ;; fq:addveza takav preskace, pa se veza ne upisuje ni u
              ;; jedno vlakno -- jer ga nema.
              ;;
              ;; Napojno vlakno je JEDNO i lezi u POSLEDNJEM redu rama.
              ;;
              ;; Ranije je islo u prvi, jer su izlazi tada rasli nanize
              ;; ispod njega.  Otkad rastu navise (odeljak 3c), prvi red
              ;; je red izlaza 1, pa bi napajanje ostalo iznad cele
              ;; lepeze i ormar bi porastao za jos toliko.  Poslednji red
              ;; je onaj koji lepeza popunjava odozgo, pa se prazno
              ;; kuciste ovim precrtavanjem popuni umesto da odskoci.
              ;;
              ;; fq:virtfl postavlja *fq-a*, ali ne dira *fq-m*, pa
              ;; ogledalno kuciste dobija tacku na svojoj strani.
              (progn
                (princ (fq:tf "osp.prazan" (list oz)))
                (setq k  1
                      fl (fq:virtfl u0 (+ vb (fq:kucmarg crt)) 1 1 1 ang)))
              ;; Ormar postavljen samostalno moze da tvrdi vise zauzetih
              ;; portova nego sto kabl ima vlakana; crtac to svakako
              ;; sabija, pa se i ovde racuna sa istim brojem.
              (setq fl (cadr pc)
                    k  (max 1 (min k (length fl) (1- kap)))))
            ;; Oba kraja kabla leze na istim visinama, pa se mora
            ;; proveriti i GDE vlakna zavrsavaju: kolona portova stoji
            ;; tacno na kraju kabla.
            (if (> (abs (- (car (fq:loc (cadr (car fl)))) u0)) 1.0)
              (princ (fq:tf "osp.ne.zavrsavaju" (list oz)))
              (progn
                ;; Napojni port se bira SAMO kod ormara.  Kutija ima sve
                ;; konektore fizicki prisutne i numerise ih unapred, pa
                ;; joj napajanje po konstrukciji ide na poslednji, na
                ;; visinu dolaznog vlakna -- vidi granu spl u fq:drawzok.
                ;; Pitanje koje ima jedan mogucan odgovor se ne postavlja.
                ;;
                ;; Prazno kuciste ga takodje ne bira: zauzetih portova
                ;; nema, pa clamp ispod svodi svaki odgovor na kap.
                ;; Pitanje bi izgledalo kao izbor, a nije.
                (if (and (= crt "odo") (not sam))
                  (progn
                    (if (not (setq pn (getint (fq:tf "osp.pitaj.port" (list (itoa kap))))))
                      (setq pn kap))
                    (setq pn  (max (- kap (1- k)) (min kap pn))
                          fdi (1+ (- kap pn))))
                  (setq pn kap fdi 1))
                (if (not (setq sp (getint (fq:tf "kuc.pitaj.odnos" (list (itoa (fq:splfit (1+ (- kap k)))))))))
                  (setq sp (fq:splfit (1+ (- kap k)))))
                ;; Koliko izlaza stvarno stane.  Ormaru ostaje kap-k
                ;; slobodnih portova iznad snopa, kutiji kap-1 iznad
                ;; napojnog -- ista mera koju fq:drawzok primeni iznutra,
                ;; pa se poruka nize slaze sa crtezom.
                (setq sp (max 1 sp)
                      no (if (= crt "odo")
                           (max 1 (min sp (- kap k)))
                           (max 1 (min sp (1- kap)))))
                ;; Isto pravilo kao u fq:kucprecrtaj: nov crtez ide samo
                ;; ako je stari ceo nestao.  Inace ostaju dva rama na
                ;; istoj tacki, oba sa svojim FIBERQ zapisom.
                ;;
                ;; Mere prozora bira fq:kucproz, ista funkcija koju zove
                ;; i fq:kucprecrtaj: kutija nosi montazu ISPOD rama, ormar
                ;; ne, pa bi zakucanih 0.15 ostavilo stari natpis montaze
                ;; da visi pod novim ramom.  Ona i SKRACUJE prozor kad
                ;; iznad ili ispod stoji tudje kuciste -- ovde je kvar bio
                ;; isti kao u fq:kucprecrtaj, jer su brojevi bili prepisani
                ;; odande.  Sada se ta dva mesta ne mogu razici.
                ;;
                ;; Jedna razlika naspram fq:kucprecrtaj ostaje, i svesna
                ;; je: ova komanda ram POVECAVA.  fq:drawodospl izlaze
                ;; crta IZNAD zatecenog vrha snopa, pa je nov vt visi od
                ;; starog, a prozor se meri po STAROM ramu.  Kad iznad
                ;; stoji tudje kuciste, ono sada prezivi brisanje -- i
                ;; nov, visi ram prodje kroz njega.
                ;;
                ;; Razmena je namerna: podatak ostaje, a dva rama koja se
                ;; preklapaju se VIDE.  Ranije je tudje kuciste nestajalo
                ;; sa svojim zapisom, i to se nije videlo nikako.  Poruka
                ;; koju fq:kucproz tada ispise govori samo o natpisima,
                ;; pa ovo nije potpuno objasnjeno korisniku; mera po ramu
                ;; koji se TEK crta bi to resila, ali trazi da se visina
                ;; lepeze izracuna dvaput -- ovde i u crtacu -- a to je
                ;; druga kopija istog racuna.
                (setq pz (fq:kucproz crt oz ul ur vt vb)
                      br (fq:kucbrisi ul ur vt vb (car pz) (cadr pz)))
                (if (/= (car br) (cdr br))
                  (fq:kucodbij oz br)
                  (progn
                    ;; Strana se namece, iz istog razloga kao u
                    ;; fq:kucspoji: brisanje je vec otislo po staroj
                    ;; geometriji, pa nov ram mora na istu stranu.
                    (setq *fq-ogl* (if ogl "da" "ne"))
                    (if (= crt "odo")
                      (fq:drawodospl oz adr kap mod mnt k sp fdi st fl ang)
                      (fq:drawzok oz adr mod kap k kon mnt sp st fl ang))
                    (princ (fq:tf "osp.dodat" (list oz (itoa pn) (itoa (+ k no)) (itoa kap))))))))))))))
  (princ))


;;; ------------------------------------------------------------------
;;;  9d. IZVOD IZ KUCISTA
;;;
;;;  Kabl koji IZ kucista ide dalje, ispod snopa koji je vec u njemu.
;;;  U praksi je to najcesci nastavak price: u ODO ulazi magistralni
;;;  kabl, a iz njega nadole odlazi razvodni.
;;;
;;;  Vlakno izvoda krece na koloni konektora, kao i svako drugo, pa
;;;  vodoravno ide do svog loma i odatle pravo nadole do zajednicke
;;;  zavrsne linije.  Zato ga kuciste vidi kao i svaki drugi kabl:
;;;  ram se rasiri, veze se upisu, FQDODAJ i FQSPOJ rade nepromenjeno.
;;;
;;;  PRVO vlakno lomi NAJDALJE od kolone, svako sledece blize njoj.
;;;  Tako se lomovi ne seku: vodoravni deo drugog vlakna staje pre nego
;;;  sto stigne do vertikale prvog.  Isto pravilo koristi i FQODVOD.
;;;
;;;  Vlaknima se upisuje ugao IZVODA, ne kucista.  Na donjem kraju je
;;;  onda sve standardno: prvo vlakno ima najvisi red, svi zavrsavaju na
;;;  istoj liniji, pa se tamo normalno kaci ODO, nastavak ili spliter.
;;;  Gornji kraj je savijen, pa njega kuciste sredjuje samo: FQIZVOD ga
;;;  precrtava odmah po crtanju kabla.
;;; ------------------------------------------------------------------

(setq *FQ-IZV-DX* 2.00)   ; razmak prve vertikale izvoda od kolone
;; Dokle se trazi sledeca zauzeta kolona ulevo.  Mora da bude vece od
;; razmaka izmedju dva susedna vlakna (*FQ-PITCH* + *FQ-GAP* = 2.8), da
;; se snop ne prekine na sred sebe, a dovoljno malo da se ne preskoci u
;; neki sasvim drugi deo crteza.
(setq *FQ-IZV-DOH* 6.00)

;; Ugao pod kojim je kuciste nacrtano, procitan iz samog rama.
;;
;; fq:Wrect pravi temena redom (ul vt) (ur vt) (ur vb) (ul vb), pa prva
;; ivica pokazuje tacno u smeru +u kucista.  Ugao se zato ne mora ni
;; pamtiti ni pogadjati, niti se mora kliknuti kabl da bi se saznao.
(defun fq:kucugao (e / pts d)
  (setq pts (fq:pl10 e))
  (if (< (length pts) 2)
    nil
    (if (setq d (fq:jed (car pts) (cadr pts))) (fq:stepen d) nil)))

;; Da li je ram OGLEDALAN, citano iz njega samog.
;;
;; Ogledalo se u crtezu ne pamti kao polje.  Nijedna vrsta kucista ne bi
;; smela da dobije novo polje -- fq:vstat im status cita kao POSLEDNJI
;; clan zapisa -- a i da sme, zatecen crtez ga ne bi imao.  Nosi ga sama
;; geometrija rama, i to potpuno.
;;
;; Sam ugao prve ivice NIJE dovoljan.  Ogledalno kuciste uz kabl pod 0 i
;; obicno kuciste uz kabl pod 180 imaju prvu ivicu na istu stranu, pa im
;; je fq:kucugao isti.  Drugo nije teorijsko: fq:pickcable uzima ugao iz
;; polja 13 vlakna, a kabl sa lomovima ume da zavrsi ulevo, pa ramova pod
;; 180 u zatecenim crtezima ima.
;;
;; Razlikuju se po SMISLU druge ivice.  fq:Wrect pravi temena redom
;; (ul vt) (ur vt) (ur vb) (ul vb), pa prva ivica ide po +u, a druga po
;; -v.  Kod obicnog rama je taj par desni (vektorski proizvod negativan),
;; kod ogledalnog levi, jer preslikavanje obrce orijentaciju ravni.  To
;; je tacno ono po cemu se ogledalo razlikuje od svake rotacije, pa je
;; provera potpuna i ne zavisi ni od kakvog spoljnog ugla.
(defun fq:ramogl (e / pts a b c ux uy vx vy)
  (setq pts (fq:pl10 e))
  (if (< (length pts) 3)
    nil
    (progn
      (setq a  (nth 0 pts)  b (nth 1 pts)  c (nth 2 pts)
            ux (- (car  b) (car  a))  uy (- (cadr b) (cadr a))
            vx (- (car  c) (car  b))  vy (- (cadr c) (cadr b)))
      (> (- (* ux vy) (* uy vx)) 0.0))))

;; Orijentacija kucista: (ugaoKabla ogledalo), ili nil kad se ne cita.
;;
;; Vraca ugao KABLA, ne ugao prve ivice: kod ogledalnog rama ivica gleda
;; na suprotnu stranu od kabla, pa se od procitanog oduzima 180.  Svaki
;; pozivalac hoce ugao kabla -- po njemu se crta ono sto se na kuciste
;; kaci -- pa se oduzimanje radi ovde, na jednom mestu.
(defun fq:kucori (e / k o)
  (setq k (fq:kucugao e))
  (if (not k)
    nil
    (progn
      (setq o (fq:ramogl e))
      (list (if o (rem (+ k 180) 360) k) o))))

;; Margina rama iznad prvog i ispod poslednjeg reda, po vrsti crtaca.
(defun fq:kucmarg (crt)
  (cond ((= crt "odf") *FQ-ODF-MARG*)
        ((= crt "odo") *FQ-ODO-MARG*)
        (T             *FQ-ZOK-MARG*)))

;; Crta kabl izvoda.  Sve se racuna u sistemu KUCISTA: u ide duz
;; ulaznog kabla, v popreko, a izvod odlazi na stranu -v, tamo gde se
;; raster i inace nastavlja.
;;   u0    kolona konektora
;;   v1    red prvog vlakna izvoda
;;   ub0   lom POSLEDNJEG vlakna, onog najblizeg koloni
;;   vend  zajednicka zavrsna linija
;;   prav  T crta samo uspravnu liniju, bez lakta ka koloni
;;
;;  Lakat ima smisla samo tamo gde postoji kolona konektora: vlakno
;;  izvoda dodje do nje i tu se prikljuci.  U nastavku konektora nema, a
;;  lakat pravi stetu pri spajanju: mesto spoja je sredina nastavka, pa
;;  vlakno prvo ode do sredine, pa se VRATI nazad do svog loma i tek
;;  onda silazi — na crtezu ispadne stepenasta petlja.
;;
;;      sa laktom                    bez lakta
;;        ====+====|                   ========|
;;        ==+=|====|                   ======+ |
;;          | |    |                     |   | |
(defun fq:drawizvod (oz disp lbl kat st hang u0 v1 ub0 vend nt nf prav
                     / sa ang rr cc zad i j n v ub id kr ue)
  (fq:init)
  (setq sa     *fq-a*
        ang    (rem (+ hang 90) 360)
        *fq-a* (fq:deg hang)
        rr     (fq:redovi nt nf v1 nil)
        cc     (fq:redovi nt nf 0.0 nil)
        zad    (fq:last cc)
        n      0
        i      1
        kr     nil)
  (while (<= i nt)
    (setq j 1)
    (while (<= j nf)
      ;; Kome ide koja kolona zavisi od toga sa koje strane vlakno
      ;; dolazi, jer se lomovi ne smeju seci:
      ;;   sa laktom  vodoravni deo ide od kolone konektora ULEVO, pa
      ;;              prvo vlakno mora najdalje, na ub0 - span;
      ;;   bez lakta  vodoravni deo dolazi tek posle spajanja, i to od
      ;;              dolaznog kabla SLEVA, pa vazi obrnuto: prvo
      ;;              vlakno staje prvo, na ub0.
      (setq n  (1+ n)
            v  (nth (1- n) rr)
            ub (if prav
                 (+ ub0 (nth (1- n) cc))
                 (- ub0 (- (nth (1- n) cc) zad)))
            id (strcat oz "/" (itoa i) "/" (itoa j)))
      ;; Bez lakta GORNJI kraj ide poslednji u listi.  fq:sporend bira
      ;; kraj po koordinati duz kabla, a kod uspravnog vlakna su oba
      ;; kraja na istoj, pa presudjuje redosled temena; bez ovoga bi
      ;; grupno spajanje hvatalo donji kraj izvoda.
      (fq:Wpl2 (if prav
                 (list (list ub vend) (list ub v))
                 (list (list u0 v) (list ub v) (list ub vend)))
               *FQ-VLACI* "FQ_VLAKNO"
               ;; namena i kat idu kao kodovi, dva prazna clana pred
               ;; vrstom su polja 16 i 17 -- vidi istu listu i puno
               ;; obrazlozenje u fq:drawkabl.  Izvod se ovde uvek crta sa
               ;; kat = "" (jedini pozivalac je nize u ovom fajlu): izvod
               ;; nije kabl iz kataloga i nema kategoriju.
               ;;
               ;; *fq-vrs* postavlja fq:kablpita, koju fq:izvodtelo zove
               ;; pre ovog crtaca, isto kao i *fq-kod*.
               (list "VLAKNO" id oz (itoa i) (itoa j) (itoa n)
                     (fq:cname j) st "" "" "nmFree" lbl kat
                     (itoa ang) *fq-kod* id "" "" *fq-vrs* ""))
      (setq kr (cons (fq:w ub vend) kr)
            j  (1+ j)))
    (setq i (1+ i)))
  (setq kr (reverse kr))

  ;; Donji kraj se crta u sistemu IZVODA.  Tamo svi krajevi leze na
  ;; istoj koordinati duz kabla, a redovi idu odozgo nanize, isto kao
  ;; kod pravog kabla, pa je to obican kraj kabla i crta ga fq:lomkraj.
  ;; Izvod ide ka manjem u, pa se kraj uvlaci unapred: smer je 1.
  (fq:lomkraj kr ang 1 nt nf)
  (setq *fq-a* (fq:deg ang)
        ue     (car (fq:loc (car kr))))
  (fq:Wtext (+ ue *FQ-KLBLDX*)
            (+ (cadr (fq:loc (car kr))) (* 1.3 *FQ-PITCH*))
            (* 1.4 *FQ-TXTH*) disp 7 "FQ_OZNAKA" "FQ_TXTB" 0 0)
  (setq *fq-a* sa)
  n)

;; Ram iz kog kabl izlazi.  Pored kucista prima i nastavak: izvod se u
;; praksi isto tako vadi iz obicnog nastavka na trasi, a ne samo iz
;; ormara.  Vraca (entitet . xdata).
(defun fq:izvpick (por / s d)
  (setq s (entsel por))
  (cond
    ((not s) (princ (fq:t "opste.nista")) nil)
    ((not (setq d (fq:xdget (car s))))
       (princ (fq:t "opste.nije.element")) nil)
    ((= (car d) "NASTAVAK")   (cons (car s) d))
    ((fq:kucvrsta d)          (cons (car s) d))
    (T (princ (fq:t "izv.nije.ram"))
       (princ (fq:t "izv.klikni"))
       nil)))

;; Najnizi red koji je unutar rama nastavka vec zauzet.
;;
;; Kod kucista se gleda kolona konektora, jer tamo vlakna zavrsavaju.
;; Kroz nastavak vlakna PROLAZE i nemaju teme unutar rama, pa se za
;; svako racuna red na sredini rama i uzima najnizi.
;;
;; Ranije nacrtan izvod se namerno NE racuna: on je uspravan, pa mu red
;; na sredini rama ispadne donji kraj kabla, daleko ispod rama, i tu
;; otpada sam.  Zato svaki izvod krece iz istog reda, a od prethodnog se
;; razlikuje po koloni.  Vraca v ili nil ako je ram prazan.
(defun fq:nastdno (ul ur vt vb / ss i e d pts ua ub uc v vmin)
  (setq ss   (fq:ssx '((8 . "FQ_VLAKNO") (-3 ("FIBERQ"))))
        i    0
        vmin nil
        uc   (* 0.5 (+ ul ur)))
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i) d (fq:xdget e) pts (fq:plpts e))
      (if (and d (= (car d) "VLAKNO") pts (> (length pts) 1))
        (progn
          (setq ua (apply 'min (mapcar 'car pts))
                ub (apply 'max (mapcar 'car pts)))
          (if (and (< ua ur) (> ub ul))
            (progn
              (setq v (cadr (fq:tackana pts (max ua (min ub uc)))))
              (if (and (>= v (- vb 0.01)) (<= v (+ vt 0.01))
                       (or (not vmin) (< v vmin)))
                (setq vmin v))))))
      (setq i (1+ i))))
  vmin)

;; Sve kolone na kojima neko vlakno silazi nadole, levo od kolone
;; konektora i ne pocinjuci iznad rama.
;;
;; Gleda se SAMO uspravan segment, ne bilo koje teme u ramu.  Teme
;; ostaje i tamo gde je vlakno preseceno — FQPRESECI ga zavrsava
;; *FQ-PRS-ULAZ* od ivice rama — i tamo gde je spojeno, jer FQSPOJ
;; ostavlja teme na sredini rama.  To nisu kolone nego obicni krajevi, i
;; da se racunaju, prvi izvod bi bezao daleko levo od rama.
(defun fq:izvkol (vt u0 / ss i e d pts j n p q out)
  (setq ss (fq:ssx '((8 . "FQ_VLAKNO") (-3 ("FIBERQ")))) i 0 out nil)
  (if ss
    (while (< i (sslength ss))
      (setq e (ssname ss i) d (fq:xdget e) pts (fq:plpts e))
      (if (and d (= (car d) "VLAKNO") pts (> (length pts) 1))
        (progn
          (setq j 0 n (1- (length pts)))
          (while (< j n)
            (setq p (nth j pts) q (nth (1+ j) pts))
            (if (and (< (abs (- (car  p) (car  q))) 0.01)
                     (> (abs (- (cadr p) (cadr q))) 0.01)
                     ;; uspravan deo koji ne dolazi odozgo
                     (<= (max (cadr p) (cadr q)) (+ vt 0.01))
                     (< (car p) (- u0 0.5)))
              (setq out (cons (car p) out)))
            (setq j (1+ j)))))
      (setq i (1+ i))))
  out)

;; Najleva vec zauzeta kolona, trazena SIRENJEM ULEVO: prvo unutar
;; rama, pa se granica pomeri na nadjenu kolonu i trazi se opet, dokle
;; god ima necega u dohvatu.
;;
;; Bez sirenja bi snop koji je vec izasao levo od uskog rama ostao
;; neprimecen, pa bi nov izvod legao tacno na njega.  Sa neogranicenim
;; trazenjem bi se pokupila uspravna linija bilo gde u crtezu, pa bi
;; snop odlutao nekud daleko levo.
(defun fq:izvlevo (ul ur vt u0 / kol gr m novo)
  (setq kol (fq:izvkol vt u0) gr nil)
  (foreach u kol
    (if (and (>= u (- ul 0.01)) (<= u (+ ur 0.01))
             (or (not gr) (< u gr)))
      (setq gr u)))
  (if gr
    (progn
      (setq novo T)
      (while novo
        (setq novo nil m nil)
        (foreach u kol
          (if (and (< u gr) (>= u (- gr *FQ-IZV-DOH*))
                   (or (not m) (< u m)))
            (setq m u)))
        (if m (setq gr m novo T)))))
  gr)

;; Kolona na kojoj krece lom POSLEDNJEG vlakna novog izvoda.  Prvi
;; izvod krece od kolone konektora, svaki sledeci se sklanja levo od
;; onoga sto je vec u ramu, i to za isti raster koji deli cevcice.
(defun fq:izvub0 (u0 lev)
  (if lev
    (min (- u0 *FQ-IZV-DX*) (- lev *FQ-PITCH* *FQ-GAP*))
    (- u0 *FQ-IZV-DX*)))

;; Zajednicki deo izvoda: pita kabl i nacrta ga.  Vraca
;; (oznaka  donji-red  broj-vlakana  duzina  tip  najleva-kolona)
;; ili nil.  upoz je T samo tamo gde ram ne moze da naraste.
(defun fq:izvodtelo (hang ul u0 ub0 v1 prav upoz
                     / r nt nf lg koz lbl st disp m rr span vend)
  (setq r    (fq:kablpita)
        nt   (nth 0 r)  nf  (nth 1 r)  lg (nth 2 r)
        koz  (nth 3 r)  lbl (nth 4 r)  st (nth 5 r)
        disp (strcat koz "   " lbl)
        m    (* nt nf)
        lg   (max lg *FQ-LEN-MIN*)
        rr   (fq:redovi nt nf v1 nil)
        span (- (car rr) (fq:last rr))
        vend (- (fq:last rr) lg))
  (if (and upoz (< (- ub0 span) ul))
    (princ (fq:tf "izv.snop.siri" (list (itoa m)))))
  (if (not (fq:velik nt nf))
    (progn (princ (fq:t "opste.otkazano")) nil)
    (progn
      (fq:drawizvod koz disp lbl "" st hang u0 v1 ub0 vend nt nf prav)
      (list koz (fq:last rr) m lg lbl (- ub0 span)))))

(defun c:FQIZVOD ( / *error* pk e d p tip crt oz kap spl hang g ul ur vt vb u0
                     marg post r koz m lg lbl v1 raw fl pit dno kw prav
                     lev ub0 er)
  (setq *error* fq:err)
  (fq:init)
  (setq pk (fq:izvpick (fq:t "izv.pitaj.ram")))
  (if (not pk)
    nil
    (progn
      (setq e    (car pk)
            d    (cdr pk)
            hang (fq:kucugao e))
      (if (not hang)
        (princ (fq:t "opste.ram.ugao"))
       ;; Izvod iz ogledalnog kucista se ODBIJA, ne crta naopako.
       ;;
       ;; Sve ispod meri u sistemu rama i lomi vlakna ka manjem u, jer
       ;; izvod izlazi na istu stranu na koju je i dolazni kabl.  Kod
       ;; ogledalnog kucista je ta strana suprotna, pa bi vlakna izvoda
       ;; usla u telo ormara -- a to se ne vidi kao greska nego kao
       ;; cudan crtez, i tek specifikacija pokaze da su veze upisane.
       ;;
       ;; Nastavak ogledalo ne poznaje (fq:drawnast nema kolonu
       ;; konektora), pa fq:ramogl nad njegovim ramom vrati nil i ova
       ;; grana ga ne dira.
       (if (fq:ramogl e)
        (princ (fq:tf "izv.ogledalo" (list (fq:f 1 d))))
        (if (= (car d) "NASTAVAK")
          ;;; --- izvod iz nastavka ------------------------------------
          (progn
            (setq oz (fq:na-oz d))
            (setq *fq-a* (fq:deg hang)
                  g      (fq:ramnast e))
            (if (not g)
              (princ (fq:t "izv.nema.temena"))
              (progn
                (setq ul (nth 0 g)  ur (nth 1 g)
                      vt (nth 2 g)  vb (nth 3 g)
                      ;; U nastavku nema kolone konektora, pa izvod
                      ;; krece odmah unutar desne ivice rama.
                      u0  (- ur *FQ-NAST-MARG*)
                      dno (fq:nastdno ul ur vt vb)
                      lev (fq:izvlevo ul ur vt u0)
                      v1  (if dno
                            (- dno *FQ-PITCH* *FQ-GAP*)
                            (- vt *FQ-NAST-MARG*))
                      ub0 (fq:izvub0 u0 lev))
                (princ (fq:tf "izv.iz.nastavka" (list oz (itoa hang))))
                (if lev
                  (princ (fq:t "izv.vec.ima")))
                ;; U nastavku nema konektora, pa lakat nema kome da
                ;; dodje: vlakna idu pravo nadole.  Ram sam naraste, pa
                ;; nema ni upozorenja da snop izlazi levo.
                (setq r (fq:izvodtelo hang ul u0 ub0 v1 T nil))
                (if r
                  (progn
                    (setq koz (nth 0 r)  m (nth 2 r)
                          lg  (nth 3 r)  lbl (nth 4 r))
                    ;; Ram nastavka se ne precrtava iz vlakana kao
                    ;; kuciste, nego mu se pomere leva i donja ivica da
                    ;; obuhvate novi snop.  Desna i gornja ostaju gde
                    ;; jesu: izvod ide dole-levo i nikad ne izlazi na
                    ;; njihovu stranu, pa idu kao nil.
                    (setq *fq-a* (fq:deg hang))
                    (if (fq:nastsiri e
                                     (- (nth 5 r) *FQ-NAST-MARG*)
                                     nil
                                     nil
                                     (- (nth 1 r) *FQ-NAST-MARG*))
                      (princ (fq:t "izv.ram.prosiren")))
                    (princ (fq:tf "izv.nacrtan"
                                  (list koz lbl oz (itoa m) (rtos lg 2 1))))
                    (princ (fq:t "izv.savet.spoji")))))))
          ;;; --- izvod iz kucista, kao i do sada ----------------------
          (progn
            (setq p    (fq:kucpolja d)
                  tip  (nth 0 p)   crt (nth 1 p)   oz  (nth 2 p)
                  kap  (nth 4 p)   spl (nth 10 p))
            (if spl
              (princ (fq:tf "izv.kutija.spliter" (list oz)))
              (progn
                (setq *fq-a* (fq:deg hang)
                      g      (fq:kucgeo crt e)
                      ul     (nth 0 g)  ur (nth 1 g)  vt (nth 2 g)
                      vb     (nth 3 g)  u0 (nth 4 g)
                      marg   (fq:kucmarg crt))
                ;; Ugao se ovde NE proverava: hang je procitan iz ovog
                ;; istog rama (fq:kucugao gore, i vec je proveren na
                ;; nil), pa je ram po definiciji u svojoj orijentaciji.
                ;; Dosadasnja provera je poredila sirinu rama sa sirinom
                ;; rekonstruisanom iz zapisa i, posto ta rekonstrukcija
                ;; za ODF nije obuhvatala desnu pregradu, obarala je
                ;; FQIZVOD iz svakog ODF-a i patch panela uz poruku o
                ;; uglu koji nikad nije ni bio sporan.
                ;;
                ;; Ostaje jedino ono sto ovde stvarno moze da bude
                ;; pokvareno: pravougaonik kome fale temena, pa bi mu
                ;; ul, ur, vt i vb bili izmisljeni.  Isto to, iz istog
                ;; razloga, proverava i fq:porram.
                (if (< (length (fq:pl10 e)) 4)
                  (progn
                    (princ (fq:t "opste.ram.temena"))
                    (princ (fq:t "izv.klikni.kuc")))
                  (progn
                    (princ (fq:tf "izv.iz.kucista" (list oz (itoa hang))))
                    ;; Lakat vodi vlakno do kolone konektora, pa se ono
                    ;; tamo moze prikljuciti.  Ko izvod vari na dolazni
                    ;; kabl, a ne prikljucuje ga, bira Prava: tada
                    ;; vlakna ne dolaze do kolone, pa se ni ram ne siri
                    ;; niti se sta prikljucuje.
                    (setq prav (fq:dvatok "izv.tok.lakat" "izv.tok.prava"
                                          "izv.pitaj.lakat" nil))
                    ;; Prvi red ide odmah ispod svega sto je vec u ramu,
                    ;; sa razmakom kao izmedju cevcica.  Prazan ram znaci
                    ;; da izvod krece od prvog reda i moze da legne na
                    ;; konektore.
                    ;;
                    ;; Kolona se cita iz uspravnih delova, pa nov izvod
                    ;; ne legne preko ranijeg ni kada je taj raniji
                    ;; nacrtan bez lakta i kuciste ga ne vidi na koloni.
                    (setq post (fq:kucvise u0 vt vb nil)
                          lev  (fq:izvlevo ul ur vt u0)
                          v1   (if post
                                 (- (cadr (fq:loc (cadr (fq:last post))))
                                    *FQ-PITCH* *FQ-GAP*)
                                 (- vt marg))
                          ub0  (fq:izvub0 u0 lev)
                          r    (fq:izvodtelo hang ul u0 ub0 v1 prav T))
                    (if r
                      (progn
                        (setq koz (nth 0 r)  m (nth 2 r)
                              lg  (nth 3 r)  lbl (nth 4 r))
                        ;; Kuciste sada mora da obuhvati i taj snop.
                        ;; Sirenje ovde nista ne pita: koliko vlakana
                        ;; izvoda ide na konektore je stvar FQDODAJ-a,
                        ;; jer se izvod obicno ne prikljucuje nego vari
                        ;; na dolazni kabl.
                        ;;
                        ;; Bez lakta vlakna ne zavrsavaju na koloni, pa
                        ;; ih precrtavanje ne bi ni prepoznalo kao snop
                        ;; tog kucista; ram se tada ne dira.
                        (if prav
                          (princ (fq:t "izv.bez.lakta"))
                          (progn
                            (setq *fq-a* (fq:deg hang)
                                  raw    (fq:cablefibers koz)
                                  fl     (if raw (fq:krajevi raw u0))
                                  pit    *FQ-PITAJ-VL*
                                  *FQ-PITAJ-VL* nil)
                            ;; fq:kucspoji sme da pukne ili da se
                            ;; otkaze usred posla.  Bez ovog catch-a
                            ;; *FQ-PITAJ-VL* ostaje nil DO KRAJA
                            ;; SESIJE, pa svako sledece sirenje kucista
                            ;; prestane da pita koliko vlakana ide na
                            ;; konektore -- i tiho uzima sve.
                            (if fl
                              (setq er (vl-catch-all-apply
                                         'fq:kucspoji
                                         (list e d fl hang nil))))
                            (setq *FQ-PITAJ-VL* pit)
                            (if (car (atoms-family 1 (list "FQ:GRESKA")))
                              (fq:greska er (fq:t "izv.prikljucivanje.ne")))))
                        (princ (fq:tf "izv.nacrtan"
                                      (list koz lbl oz (itoa m)
                                            (rtos lg 2 1))))
                        (princ (fq:t "izv.donji.kraj"))))))))))))))
  (princ))


;;; ------------------------------------------------------------------
;;;  9c. OPIS VLAKNA
;;;
;;;  Namena vlakna (radno, planirano, rezervisano...) je boja, a ovo je
;;;  tekst: za koga je vlakno, odakle dokle ide, koji je broj zahteva.
;;;  U crtezu stoji iznad same linije vlakna, kao sto se i rucno pise:
;;;
;;;    6  Zahtev 2026-0143 - ODO 1.4, stambena zgrada, ulaz B
;;;    ---------------------------------------------------------------
;;;
;;;  Tekst se pamti u zapisu vlakna, polje 16, a rukohvat nacrtanog
;;;  teksta u polju 17.  Zapis vlakna ima status u polju 7, ne u
;;;  poslednjem (vidi fq:vstat), pa dodavanje polja na kraj nista ne
;;;  pomera; starija vlakna vracaju "" i rade normalno.
;;;
;;;  Rukohvat se pamti da bi ponovni upis MENJAO postojeci tekst
;;;  umesto da crta drugi preko njega.
;;; ------------------------------------------------------------------

(defun fq:vl-opis (d) (fq:f 16 d))
(defun fq:vl-oph  (d) (fq:f 17 d))

;; Kao fq:xdput, ali produzava zapis ako je kraci od trazenog polja.
;; fq:xdput samo zamenjuje postojece polje, pa vlaknima iz starijih
;; crteza ne bi mogao da doda polje 16.
(defun fq:xdset (e n v / d i out)
  (if (setq d (fq:xdget e))
    (progn
      (while (< (length d) (1+ n)) (setq d (append d (list ""))))
      (setq i 0 out nil)
      (foreach s d
        (setq out (cons (if (= i n) v s) out)
              i   (1+ i)))
      (fq:xd e (reverse out))
      T)))

;; Brise ranije nacrtan opis ovog vlakna, ako ga ima.
;;
;; Tri zamke:
;;   - handent nad nepostojecim rukohvatom baca gresku;
;;   - entdel nad VEC obrisanim entitetom ga VRACA u crtez, a entget
;;     nad obrisanim vraca nil, pa se time proverava da postoji;
;;   - obican AutoCAD COPY vlakna kopira i xdata, dakle i rukohvat, pa
;;     bi kopija obrisala tekst ORIGINALNOG vlakna.  Zato se brise samo
;;     tekst koji stvarno lezi na ovom vlaknu, sto se vidi po visini.
;; TRI ishoda, ne dva, i razlika medju njima je sustinska:
;;   nil            nema sta da se brise -- ili nema rukohvata, ili tekst
;;                  ne lezi na ovom vlaknu (kopirano vlakno, vidi gore)
;;   "zakljucano"   naslo se, ali entdel nije prosao
;;   T              obrisano
;;
;; Do 1.0.0 je vracalo T i u drugom slucaju, a c:FQOPIS rezultat nije ni
;; citao: brisao bi polje 17 i kad tekst i dalje stoji u crtezu.  Time
;; rukohvat nestaje, tekst ostaje, i vise ne postoji nijedan put od
;; vlakna do njega -- ni FQOPIS ga ne vidi da bi ga zamenio.
(defun fq:opisbrisi (d v / h e td)
  (setq h (fq:vl-oph d))
  (if (= h "")
    nil
    (progn
      (setq e (vl-catch-all-apply 'handent (list h)))
      (if (and e (not (vl-catch-all-error-p e)) (setq td (entget e))
               (< (abs (- (cadr (fq:loc (fq:txtpt td)))
                          (+ v (* 0.30 *FQ-PITCH*))))
                  (* 0.5 *FQ-PITCH*)))
        (if (fq:brisi e) T "zakljucano")
        nil))))

;; Crta opis iznad vlakna i vraca rukohvat nacrtanog teksta.
;;   u  mesto pocetka teksta duz vlakna, iz tacke koju je korisnik
;;      pokazao, pa opis stoji tamo gde ima mesta
;;   v  visina vlakna
;;
;; Rukohvat dolazi iz same primitive, NE iz (entlast).  Ovde je to
;; najvaznije u celom projektu: rukohvat se upisuje u polje 17 vlakna, a
;; sledeci FQOPIS ga procita i entdel-uje.  Da (entlast) posle neuspelog
;; entmake vrati prethodni entitet, vlakno bi nosilo rukohvat TUDJEG
;; crteza i FQOPIS bi ga obrisao.  To nije pogresan zapis nego brisanje
;; tudjeg crteza, i nista u crtezu ne bi pokazalo odakle je doslo.
;;
;; nil je ispravan povratak: c:FQOPIS ga vec ocekuje, `(if h h "")`.
(defun fq:opiscrtaj (s u v ang / sa e)
  (setq sa     *fq-a*
        *fq-a* (fq:deg ang)
        e      (fq:Wtext u (+ v (* 0.30 *FQ-PITCH*)) *FQ-TXTH*
                         s 7 "FQ_OZNAKA" "FQ_TXT" 0 0)
        *fq-a* sa)
  (if e (cdr (assoc 5 (entget e))) nil))

(defun c:FQOPIS ( / *error* s e d ang lc u v old novi h obr)
  (setq *error* fq:err)
  (fq:init)
  (setq s (entsel (fq:t "ops.pitaj.vlakno")))
  (cond
    ((not s) (princ (fq:t "opste.nista")))
    ((not (and (setq d (fq:xdget (setq e (car s))))
               (= (car d) "VLAKNO")))
       (princ (fq:t "opste.nije.vlakno.kabla")))
    (T
       (setq ang (fq:vl-ang d)
             *fq-a* (fq:deg ang)
             lc  (fq:loc (cadr s))
             u   (car lc)
             old (fq:vl-opis d))
       (if (/= old "") (princ (fq:tf "ops.sada.pise" (list old))))
       (setq novi (getstring T (fq:t "ops.pitaj.opis")))
       ;; Visina se uzima sa same linije vlakna, ne iz klika, da tekst
       ;; ne bi stajao koso u odnosu na vlakno.  Ali NA KLIKNUTOM u, ne
       ;; sa najblizeg TEMENA.
       ;;
       ;; Do 25.08.2026 je ovde stajalo (fq:najbliza e (cadr s)), a ta
       ;; funkcija vraca najblize teme polilinije.  Kod vodoravnog
       ;; vlakna se razlika ne vidi -- svaka tacka ima isti v.  Kod
       ;; vlakna koje ulazi u kuciste temena su na koloni konektora, pa
       ;; je opis skakao tamo umesto da ostane gde je kliknuto.
       ;;
       ;; fq:tackana radi tacno ovo: tacka putanje na datoj koordinati
       ;; duz vlakna.  Ista funkcija cuva i fq:varnavl.
       (setq v (cadr (fq:tackana (fq:plpts e) u)))
       (setq obr (fq:opisbrisi d v))
       ;; Ako stari tekst nije obrisan, NE dira se ni polje 17 ni polje
       ;; 16.  Inace bi u oba slucaja tekst ostao u crtezu bez ijednog
       ;; puta nazad do njega: kod praznog unosa zato sto se rukohvat
       ;; brise, a kod novog zato sto ga nov rukohvat prepisuje.
       ;; fq:xd je vec javio koji je lejer i sta mu je.
       (cond
         ((equal obr "zakljucano")
          (princ (fq:t "ops.nije.obrisan")))
         ((= novi "")
          (fq:xdset e 16 "")
          (fq:xdset e 17 "")
          (princ (fq:t "ops.obrisan")))
         (T
          (setq h (fq:opiscrtaj novi u v ang))
          (fq:xdset e 16 novi)
          (fq:xdset e 17 (if h h ""))
          (princ (fq:tf "ops.upisano" (list novi)))))))
  (princ))

;; Teme polilinije koje je najblize datoj tacki, u svetskim koordinatama.
;; Vlakno ume da bude prelomljeno (vertikalni odvod), pa se visina uzima
;; sa onog dela na koji je korisnik kliknuo.
(defun fq:najbliza (e p / best bd dd)
  (setq best nil bd nil)
  (foreach q (fq:pl10 e)
    (setq dd (distance q p))
    (if (or (not bd) (< dd bd)) (setq best q bd dd)))
  (if best best p))


;;; ------------------------------------------------------------------
;;;  10. CUVANJE KATALOGA
;;;
;;;  Isti obrazac kao fq:ccsave i fq:ccload za color code standarde:
;;;  katalog se pise kao obican LISP izraz, pa se ucitava sa load.
;;; ------------------------------------------------------------------

;;  Verzija ISPORUCENOG kataloga, ne formata fajla.  Podize se kad god
;;  se u *FQ-KUCISTA* doda nova VRSTA kucista -- ne kad se promeni neko
;;  postojece polje.
;;
;;  Cemu sluzi: fq:katload radi TOTALNU zamenu kataloga procitanim
;;  fajlom.  Bez oznake verzije, svaka nova vrsta iz neke sledece verzije
;;  FiberQ-a NIKAD ne bi stigla do korisnika koji je ijednom pozvao
;;  FQKATSAVE -- njegov fajl bi je zauvek prekrivao.  Ista bolest je vec
;;  jednom kostala: sacuvan fq-boje.dat je prekrio ugradjenu IEC
;;  ispravku i to se videlo tek kad je korisnik prijavio pogresne boje.
;;
;;  "1" je fajl BEZ ove linije, dakle svaki zatecen.  Stariji citac ovu
;;  liniju cutke preskace: u njegovom cond-u nema grane koja se poklapa,
;;  pa vraca nil.  Format se time ne menja u smislu pravila iz CLAUDE.md.
;;
;;  IME NE SME DA SADRZI PODNIZ "*FQ-KUCISTA*" ni "*FQ-KONEKTORI*":
;;  fq:katparse otvara blok preko vl-string-search, pa bi takvo ime
;;  pocelo da cita zapise kucista iz linije koja to nije.
(setq *FQ-KAT-VERZIJA* "2")

;; Koliko je vrsta dopisano iz isporucenog kataloga pri poslednjem
;; ucitavanju.  Cita se dole, u poruci pri ucitavanju fajla.
(setq *fq-kat-dop* 0)

(defun fq:katpaths ( / l p)
  (setq l nil)
  (if (setq p (getvar "ROAMABLEROOTPREFIX"))
    (setq l (cons (strcat p "Support\\fq-kucista.dat") l)))
  (if (setq p (getenv "APPDATA"))
    (setq l (cons (strcat p "\\FiberQ\\fq-kucista.dat") l)))
  (reverse l))

(defun c:FQKATSAVE ( / *error* f p k r ok pr)
  (setq *error* fq:err)
  (setq ok nil)
  (foreach p (fq:katpaths)
    (if (not ok)
      (progn
        (setq pr (fq:tmpime p)
              f  (vl-catch-all-apply 'open (list pr "w")))
        (if (and f (not (vl-catch-all-error-p f)))
          (progn
            (write-line ";;; FiberQ katalog kucista, pravi FQKATSAVE" f)
            ;; Verzija ide PRE blokova, u svoj red.  Citac je uzima
            ;; preko read, bez izvrsavanja, kao i sve ostalo u fajlu.
            (write-line (strcat "(setq *FQ-KAT-VERZIJA* \""
                                *FQ-KAT-VERZIJA* "\")") f)
            (write-line "(setq *FQ-KONEKTORI* '(" f)
            ;; vl-prin1-to-string, a ne rucno lepljenje navodnika:
            ;; konektor sa navodnikom ili obrnutom crtom u imenu je
            ;; ranije pravio fajl koji se ne moze procitati nazad, i to
            ;; se videlo tek pri sledecem pokretanju
            (foreach k *FQ-KONEKTORI*
              (write-line (strcat "  " (vl-prin1-to-string k)) f))
            (write-line "))" f)
            (write-line "(setq *FQ-KUCISTA* '(" f)
            (foreach r *FQ-KUCISTA*
              (write-line (strcat "  " (vl-prin1-to-string r)) f))
            (write-line "))" f)
            (close f)
            (if (fq:zameni p pr) (setq ok p)))))))
  (if ok
    (princ (fq:tf "kat.sacuvan" (list ok)))
    (princ (fq:tf "kat.ne.sacuvan" (list (car (fq:katpaths))))))
  (princ))


;;; ------------------------------------------------------------------
;;;  CITAC KOJI NE IZVRSAVA ONO STO PROCITA
;;;
;;;  fq:katload je do 1.0.0 radio (load) nad korisnickim .dat fajlom.
;;;  To znaci: sve sto se u tom fajlu nadje izvrsava se sa punim pravima
;;;  AutoLISP-a, pri svakom pokretanju AutoCAD-a, pre nego sto korisnik
;;;  i vidi komandnu liniju.  Fajl lezi u profilu, dakle u njega pise
;;;  svaki program koji radi pod tim nalogom.  Za Autodesk App Store
;;;  reviziju je to nalaz sam po sebi; i bez nje, dodatak ne treba da
;;;  bude put kojim tudji kod ulazi u CAD.
;;;
;;;  FORMAT SE NE MENJA.  Starija instalacija na istoj masini deli isti
;;;  profil i mora da procita isti fajl.  Zato citac prati tacan oblik
;;;  koji pise c:FQKATSAVE: red koji sadrzi ime globalne otvara blok,
;;;  red "))" ga zatvara, a izmedju je po jedan zapis u svom redu.
;;;
;;;  fq:trim se NE koristi iako postoji: on je u fq-sema.lsp, a ovo se
;;;  izvrsava na nivou fajla, pa bi kod ucitavanja po abecedi
;;;  (fq-kucista pre fq-sema) tiho ostavio korisnika bez kataloga.
;;; ------------------------------------------------------------------

;; Zapis mora imati BAR osam clanova, ne tacno osam: cim neka kasnija
;; verzija doda deveto polje, "tacno osam" bi tiho odbacio ceo korisnicki
;; katalog.  vl-list-length vraca nil za nepravilnu listu, pa se vrednost
;; prvo uhvati pa poredi -- (>= nil 8) obara ucitavanje.
;;
;; Crtac se proverava kao string, ne prema spisku '("odf" "odo" "zok"):
;; taj spisak vec stoji na petnaest mesta u ovom fajlu, a kopija ovde bi
;; tiho odbacila ceo katalog cim se doda cetvrti crtac.
(defun fq:katok (r / n)
  (and (= (type r) 'LIST)
       (setq n (vl-list-length r))
       (>= n 8)
       (= (type (nth 0 r)) 'STR)     ; kljuc
       (= (type (nth 1 r)) 'STR)     ; naziv
       (= (type (nth 2 r)) 'STR)     ; crtac
       (= (type (nth 3 r)) 'STR)     ; prefiks
       (listp (nth 4 r))             ; kapaciteti
       (listp (nth 5 r))             ; modeli, sme biti prazna lista
       (listp (nth 6 r))             ; montaze
       (= (type (nth 7 r)) 'INT)))   ; splmin

;; Vraca (konektori kucista verzija) ili nil.  Nista ne upisuje u
;; globalne.  Verzija je "1" kad je u fajlu nema -- svaki zatecen fajl.
(defun fq:katparse (p / f s tt blok kon kuc ver x)
  (setq kon nil kuc nil blok nil ver "1")
  (if (setq f (open p "r"))
    (progn
      (while (setq s (read-line f))
        (setq tt (vl-string-trim " \t" s))
        (cond
          ((= tt "") nil)
          ((= (substr tt 1 1) ";") nil)
          ;; vl-string-search, a ne wcmatch: ime globalne je puno
          ;; zvezdica, a one su u wcmatch-u dzokeri
          ;;
          ;; Verzija ide PRVA u ovom cond-u, iako se nijedno od tri imena
          ;; ne sadrzi u drugom -- da bi ovaj red ostao tacan i ako se
          ;; neko cetvrto ime jednog dana doda nespretno.
          ((vl-string-search "*FQ-KAT-VERZIJA*" (strcase tt))
            (setq x (vl-catch-all-apply 'read (list tt)))
            (if (and (not (vl-catch-all-error-p x))
                     (listp x)
                     (= (type (caddr x)) 'STR))
              (setq ver (caddr x))))
          ((vl-string-search "*FQ-KONEKTORI*" (strcase tt)) (setq blok 'kon))
          ((vl-string-search "*FQ-KUCISTA*"   (strcase tt)) (setq blok 'kuc))
          ((= tt "))") (setq blok nil))
          (blok
            (setq x (vl-catch-all-apply 'read (list tt)))
            (if (not (vl-catch-all-error-p x))
              (if (= blok 'kon)
                (if (= (type x) 'STR) (setq kon (cons x kon)))
                (if (fq:katok x)     (setq kuc (cons x kuc))))))))
      (close f)
      (list (reverse kon) (reverse kuc) ver))
    nil))

;; Vrste iz ISPORUCENOG kataloga kojih u korisnickom nema, dopisane na
;; kraj.  Postojeci zapisi se NE diraju -- oni su korisnikov podatak i
;; mogu da nose modele koje je sam upisao.
;;
;; Poredi se kljuc (nth 0), i to preko strcase, jer isto radi i fq:kc.
;;
;; Zasto ovo uopste treba: bez njega korisnik koji je jednom pozvao
;; FQKATSAVE nikad ne dobija vrstu dodatu u nekoj sledecoj verziji.
;; Zasto NIJE bezuslovno, nego samo kad se verzija ne poklapa: rucno
;; obrisana vrsta bi se inace vracala pri svakom pokretanju, i korisnik
;; je ne bi mogao ukloniti nikako.
(defun fq:katspoj (kor isp / out kljuc n)
  (setq kljuc (mapcar '(lambda (x) (strcase (car x))) kor)
        out   (reverse kor)
        n     0)
  (foreach r isp
    (if (not (member (strcase (car r)) kljuc))
      (setq out (cons r out) n (1+ n))))
  (setq *fq-kat-dop* n)
  (reverse out))

;; Sve ili nista.  Stari (load) je prolazio kroz prvi setq pa padao na
;; drugom, i katalog je ostajao pola korisnicki pola fabricki -- a
;; FQVERZIJA je pritom tvrdila da je fabricki.
(defun fq:katload ( / p r x)
  (setq r nil *fq-kat-dop* 0)
  (foreach p (fq:katpaths)
    (if (and (not r) (findfile p))
      (progn
        (setq x (vl-catch-all-apply 'fq:katparse (list p)))
        (if (and (not (vl-catch-all-error-p x)) x (cadr x))
          (progn
            (if (car x) (setq *FQ-KONEKTORI* (car x)))
            ;; *FQ-KUCISTA* je u ovom trenutku jos ISPORUCEN katalog --
            ;; ova funkcija se zove na nivou fajla, ispod njegovog setq.
            ;; Zato se spajanje racuna ovde i nigde drugde.
            (setq *FQ-KUCISTA*
                  (if (= (caddr x) *FQ-KAT-VERZIJA*)
                    (cadr x)
                    (fq:katspoj (cadr x) *FQ-KUCISTA*)))
            (setq r p))))))
  r)

(setq *fq-kat-ucit* (vl-catch-all-apply 'fq:katload nil))
(if (vl-catch-all-error-p *fq-kat-ucit*) (setq *fq-kat-ucit* nil))


;;; --- baner ---------------------------------------------------------
;;;
;;;  Straza nad fq:t, iz istog razloga kao u fq-sema.lsp: ovo je jedini
;;;  tekst ovog fajla na nivou fajla, pa jedini koji izostanak
;;;  fq-jezik.lsp moze tiho da obori zajedno sa svim defun-ima ispod.
;;;  Ovde tim pre, jer se ovaj fajl ucitava PRE fq-sema.lsp.
(if (car (atoms-family 1 (list "FQ:T")))
  (progn
    (princ (strcat "\nFiberQ CAD " *FQ-VERZIJA* (fq:t "start.kucista")))
    (princ (fq:t "start.kuc.komande"))
    (if *fq-kat-ucit*
      (princ (fq:tf "start.kat.ucitan" (list *fq-kat-ucit*))))
    ;; Dopisane vrste se prijavljuju izricito.  Tiho spajanje bi znacilo
    ;; da se katalog promenio a korisnik ne zna zasto, pa bi FQKATSAVE
    ;; zapisao nesto sto nije ni pogledao.
    (if (and (boundp '*fq-kat-dop*) *fq-kat-dop* (> *fq-kat-dop* 0))
      (princ (fq:tf "start.kat.dopisano" (list (itoa *fq-kat-dop*))))))
  (princ (strcat "\nFiberQ CAD " *FQ-VERZIJA*
                 " -- fq-kucista.lsp loaded.  fq-jezik.lsp not found,"
                 " messages stay in English.")))
(princ)
