|
Lisp »Tips 'n Tricks »Tutorial di Claudio Rivoira (3) »1 | 2
Eccoci giunti alla terza puntata di questo tutorial. Questa volta il programma presentato è piuttosto complesso, ma sarà molto utile in svariate occasioni.
Come al solito occorre caricarlo con (load "c:/percorso/tutorial3") oppure dal menu strumenti, e poi lanciarlo dalla riga di comando digitando TUTORIAL3.
Per questa volta ho preparato un disegno (tutorial3.dwg in formato Autocad 2000), che contiene semplicemente una serie di testi e delle entità varie (linee e cerchi). Ovviamente questo è solo un esempio: il programma funziona con qualunque disegno.
Dopo il lancio, viene chiesto di selezionare delle entità sul disegno; la selezione può avvenire in qualunque modo: cliccando su ogni entità oppure con una finestra. La selezione si concluderà premendo INVIO.
Di tutte le entità selezionate, il programma terrà in considerazione solo i testi (normale = "TEXT" e multilinea = "MTEXT") mentre i cerchi e le linee verranno ignorati.
I testi selezionati, verranno raggruppati in una lista (TLST) denominata "Lista non Ordinata", in quanto l'ordine con cui verranno disposti nella lista, dipende da come sono stati selezionati o per la loro posizione all'interno del database di Autocad.
I testi possono essere NUMERI REALI (con la virgola), NUMERI INTERI (senza la virgola), STRINGHE ALFABETICHE (Testi composti solo da lettere e non da numeri, sia Maiusoli che minuscoli), STRINGHE ALFANUMERICHE (Testi composti da numeri e lettere).
La subrutine offre la possibilità di selezionare 2 modalità di ordinamento: STRINGHE o NUMERI
- Con la prima modalità (STRNUM = 1) gli elementi vengono valutati come stringhe e questo comporta ad es. che la stringa "01" sia diversa da "1".
- Con la seconda modalità (STRNUM = 2) tutti gli elementi vengono trasformati in numero reale, prima del confronto: "01" trasformato in numero reale diventa 1.0 ed anche "1" diventa 1.0. Inoltre tutte le stringhe alfabetiche diventano 0.0 e quindi non vengono ordinate.
E' interessante provare ad inserire come testi, tutte le combinazioni che vengono in mente: testi che iniziano con un numero, che hanno un numero all'interno o che lo hanno alla fine; testi con varie combinazioni di maiuscolo e minuscolo ecc.
Il disegno TUTORIAL3.DWG serve proprio per capire quanto siano diversi gli ordinamenti nelle 2 modalità; per questo motivo, al termine dell'ordinamento, Autocad passa in modo testo (textscr) e visualizza la lista non ordinata (TLST) e la lista ordinata (ORLST) nelle 2 modalità previste.
Vorrei far notare che selezionando la colonna centrale sul disegno TUTORIAL3.DWG, che contiene esclusivamente testi alfabetici, l'ordinamento con la modalità 2 (ordinamento numerico) non ha alcun effetto: i testi non vengono ordinati dato che la loro trasposizione in numero reale è sempre 0.0.
Nella prima colonna a sinistra del disegno tutorial3.dwg, troviamo in alto 43 e 18, in realtà questi 2 numeri fanno parte di un solo testo multilinea (viene considerato "43\\P18" in cui \\P indica "testo a capo"), di conseguenza verrebbe ordinato tenendo in considerazione solo il numero 43 iniziale. Per ovviare a questo problema ho creato una subrutine chiamata SPEZZATXT. In questa sub. viene analizzato l'MTEXT e spezzato in stringhe singole ogni volta che si trova "\\P" (da notare che i due caratteri \ vengono interpretati come uno solo).
L'ultima colonna a destra è in realtà un singolo testo multilinea, che viene spezzato ed ordinato.
ATTENZIONE:
All'ultimo minuto ho riscontrato che in certi casi particolari si ottenevano risultati errati: talvolta le liste non venivano ordinate se erano composte da pochi elementi. Dopo aver cercato inutilmente di correggere l'errore ho adottato una soluzione drastica: ho aggiunto 3 stringhe fittizzie ("***") che poi vengono eliminate con la sub. DEPURA.
E' proprio vero quello che recita una delle leggi di Murphy : "C'e' sempre un altro bug" ... un bel conforto per i programmatori !
ANALISI DEL PROGRAMMA
Analizziamo innanzitutto la selezione delle entità. Il programma è fatto in modo da permettere la selezione di qualunque entità: linee, cerchi, polilinee, testi singoli e testi multilinea, ma solo gli ultimi 2 vengono accettati. Per far questo occorre utilizzare dei FILTRI (qui le sigarette non c'entrano nulla !). Per "Filtrare" le entità selezionate esistono 2 metodi:
- Il primo consente la selezione di tutte le entità ed opera poi il filtraggio in un secondo momento.
- Il secondo (molto più raffinato), permette il filtraggio grazie ad una lista-filtro già al momento della selezione: lo si può notare dal fatto che includendo in un unica finestra di selezione i testi e le altre entità, queste ultime non vengono evidenziate.
Il primo metodo è stato disabilitato (con una serie di ";") per consentire l'utilizzo del secondo metodo.
Provando ad aprire tutorial3.lsp con un comune editor di testi (Notepad) e facendolo scorrere fino a
; ******************************************************
; FILTRI DI SELEZIONE DELLE ENTITA' (VEDI TUTORIAL3.TXT)
; ******************************************************
|
si trova l'istruzione (disabilitata con ;;;)
con la quale autolisp richiede la selezione delle entità; poi saltiamo alla sub. SCORRE e troviamo :
; ********************************************************
;;;(if (or FILTRO (Vedi Tutorial3.txt)
;;; (= (cdr (assoc 0 ent)) "TEXT") Lascia passare solo
;;; le entita' TEXT
;;; (= (cdr (assoc 0 ent)) "MTEXT")... oppure le MTEXT
;;; (multilinea)
;;; )
; ********************************************************
|
Questo è il filtro che lascia passare solo i Testi singoli e quelli multilinea tralasciando le altre entità.
Per vedere il secondo metodo, torniamo a:
; ********************************************************
; FILTRI DI SELEZIONE DELLE ENTITA' (VEDI TUTORIAL3.TXT)
; ********************************************************
|
ed analizziamo il comando:
(setq sel(ssget ; Seleziona le entita' (CON FILTRO)
'((-4 . "<OR")
(0 . "TEXT") ;Lascia passare solo le entita' TEXT
(0 . "MTEXT");... oppure le MTEXT (multilinea)
(-4 . "OR>"))
)
)
|
In cui :
(setq sel(ssget ;Serve per assegnare il set di selezione
;alla variabile SEL
'((-4 . "<OR");Segna l'inizio di un operazione Booleana OR.
(0 . "TEXT") ;Lascia passare solo le entita' TEXT
(0 . "MTEXT") ;... oppure le MTEXT (multilinea)
(-4 . "OR>")) ;Segna la fine di un operazione Booleana OR.
|
Dato che dobbiamo accettare sia i "TEXT" che gli "MTEXT", queste due liste vengono inserite fra "<OR" e "OR>": è come dire ad Autolisp << Seleziona le entità "TEXT" oppure (OR) le entità "MTEXT" >>.
Le liste con "<OR" e "OR>" sono precedute da un codice di gruppo particolare (-4) che serve proprio per le operazioni Booleane, mentre "TEXT" ed "MTEXT" hanno codice di gruppo 0 che indica l'entità; se avessimo dovuto cercare un Layer, il codice sarebbe stato 8, mentre per un colore il codice sarebbe 62 ecc.
La parte più importante del programma è la routine di ordinamento. La lista TLST viene analizzata e, con vari passaggi, trasformata nella lista ORLST (Lista ordinata).
E' interessante capire l'algoritmo (cioè la formula) utilizzato per ottenere l'ordinamento della lista.
- Vengono analizzati i primi 2 elementi della lista (es. "18" e "5")
- Se (come in questo esempio) il Secondo è Minore del Primo, vengono copiati invertiti su un'altra lista, altrimenti vengono copiati così come sono.
- Poi si passa ad analizzare il Terzo ed il Quarto elemento, ed anche qui si stabilisce se copiarli così come sono oppure in ordine invertito.
- Poi si prosegue con il Quinto ed il Sesto ... e si prosegue così fino alla fine della lista.
- Se il numero di elementi è dispari, l'ultimo lo si copia nella nuova lista senza confrontarlo con nessuno.
- Al termine del primo giro la lista nuova sarà già ... un po' piu ordinata della precedente, ma il lavoro ovviamente non sarà ancora finito.
- Si ricomincia daccapo, ma questa volta non si confrontano il Primo con il Secondo, il Terzo con il Quarto ecc. perché li troveremmo già tutti in ordine. Così ricopiamo sulla nuova lista il primo elemento e passiamo a confrontare il Secondo con il Terzo, poi il Quarto con il Quinto ecc. fino al termine della lista.
- Al terzo giro, ricominciamo a confrontare Primo-Secondo Terzo-Quarto ecc.
- Quando finiscono questi confronti ? Ovviamente quando la lista è ordinata. Per stabilire quando la lista è perfettamente ordinata, ho inserito un FLAG (un segnalino) cioè la variabile INDIOR. Questa variabile viene posta a NIL (valore nullo), ma è sufficiente un solo scambio nel confronto fra gli elementi, per portarla a T (True = vero). Quindi il programma al termine di ogni giro (LOOP), verifica il valore assunto dalla variabile INDIOR: se è rimasta a NIL, significa che la lista è ordinata e può uscire dal loop.
La situazione peggiore è quella in cui il valore più piccolo è inizialmente alla fine della lista (oppure il più grande all' inizio), cioè questi valori si trovano nel punto più lontano possibile dal punto in cui vanno posizionati. Questa situazione, non è nient'altro che il numero di testi stessi + 1: se abbiamo selezionato 100 testi ed il numero "1" (il valore più piccolo fra quelli selezionati) si trova alla fine della lista, occorreranno 101 passaggi per riportarlo al suo posto. Fortunatamente (come ho detto in precedenza), il loop può terminare anche prima grazie alla variabile INDIOR.
Un altra piccola subrutine inserita in questo programma, permette di verificare il lavoro svolto: quando il numero di testi selezionati è piuttosto elevato (500 - 600 elementi) Autocad rimane bloccato per qualche minuto e non si riesce a capire se sta lavorando o se è andato in "Crash".
La subrutine PERCENTUALE, serve proprio per visualizzare sulla riga di comando, la percentuale di lavoro svolto. Purtroppo è poco precisa ed è probabile che il lavoro venga terminato quando sulla riga di comando è visualizzato 80 o 90 %. Il motivo di questo è dovuto al fatto che per stabilire una percentuale precisa, occorrerebbe verificare uno ad uno ogni elemento memorizzando per ogniuno la posizione attuale in relazione alla posizione che devono assumere. Per esempio si dovrebbe stabilire che il n. "1" si trova al Quinto posto, ma non sono sufficienti 5 passaggi, dato che il n. "2" si trova al 18° ecc.
Alla fine questi calcoli sarebbero più lunghi dell'ordinamento stesso, quindi ho scelto di utilizzare come termine di paragone il numero di elementi della lista.
La routine PERCENTUALE non è altro che una semplice proporzione:
N.Elementi : 100 = Indice : X
... Il prodotto dei medi è uguale al prodotto degli estremi (diceva la Prof. di matematica !)
100 x Indice
X = ------------
N.Elementi
|
Per complicare un po' le cose, ho pensato di non visualizzare tutte le percentuali, ma solo quelle stabilite da un'altra variabile (Incremento).
La "X" calcolata prima viene divisa per il valore assegnato ad INCREMENTO; del numero reale (con virgola) ottenuto vengono scartate le cifre dopo la virgola (viene trasformato in Intero), quindi viene nuovamente moltiplicato per INCREMENTO.
Facciamo un esempio:
X = 38 Incremento = 10
38 : 10 = 3.8 (Reale) -> 3 (Intero) -> 3 x 10 = 30
|
Quindi viene visualizzato 30%
X = 39 Incremento = 10
39 : 10 = 3.9 (Reale) -> 3 (Intero) -> 3 x 10 = 30
|
Viene nuovamente visualizzato 30%
X = 40 Incremento = 10
40 : 10 = 4.0 (Reale) -> 4 (Intero) -> 4 x 10 = 40
|
Ora viene visualizzato 40%
Da questi esempi si capisce una cosa importante : solo quando il valore di X è divisibile per l'Incremento, la trasformazione da Reale ad Intero non scarta alcuna cifra e di conseguenza si passa ad un valore superiore. Quindi con un Incremento pari a 10 vengono visualizzate solo le percentuali 10% - 20% - 30% ecc.
Queste 2 formule riunite in una sola e trasformate nel formato RPN (Notazione Polacca Inversa) che piace tanto ad Autolisp (un po' meno ai programmatori dello stesso ...):
(setq perc (* (fix (/ (/ (* 100 indice) num) incremento)) incremento))
In cui:
(* 100 indice) Moltiplica 100 per l'indice ...
(/ (* 100 indice) num) ... divide il risultato per il n. di elementi ...
(/ (/ (* 100 indice) num) incremento) ... poi lo divide per INCREMENTO ...
(fix (/ (/ (* 100 indice) num) incremento)) ... poi lo trasforma in numero intero ...
(* (fix (/ (/ (* 100 indice) num) incremento)) incremento) ... poi lo rimoltiplica per INCREMENTO ...
(setq perc (* (fix (/ (/ (* 100 indice) num) incremento)) incremento)) ... ed infine lo memorizza nella variabile PERC.
Sito web dell'Autore
Lisp »Tips 'n Tricks
|
|