Il Bar dell'Ingegneria

Algoritmi: Offset di un poligono

« Older   Newer »
 
  Share  
.
  1.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Altra curiosità:

    Domanda: dopo aver eseguito un offset di un poligono (non intrecciato) a pescepalla con valore assai grande dell'offset (per esempio 500 cm su poligono coi lati dell'ordine massimo del decimetro), per esempio verso l'esterno (flag positivo), ed ottenuto il poligono offsettato, se eseguiamo su quest'ultimo l'offset al contrario (offset 500 e flag -1), riotteniamo il pescepalla di partenza?
     
    Top
    .
  2.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    é possibile tornare alla figura originaria con altro offset solo e soltanto se col primo offset non si è prodotto alcun intreccio. Altrimenti l'operazione inversa dell'offset produce altro poligono intrecciato diverso dall'originario. Quindi dall'antipescepalla non è più possibile tornare al pescepalla mediante operazione di offset.

    Se offsettiamo verso l'interno un poligono convesso, accade la stessa cosa. Se l'offset è tale da produrre un poligono intrecciato, non sarà piu possibile riottenere il poligono di partenza mediante altro offset. Se invece eseguiamo un offset verso l'esterno di un poligonoconvesso è sempre possibile tornare al poligono originario con un offset inverso.
     
    Top
    .
  3.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Quando ad un vertice concorrono due lati sovrapposti la funzione va in errore.
    Per evitare questo errore abbiamo sostanzialmente due vie:
    1- intercettarlo e interrompere l'esecuzione della funzione rinunciando all'offset
    2- gestire questa aberrazione

    Il caso 1 è semplice.
    Il caso 2 invece prevede la scelta di una strategia da seguire

    Vediamo un esempio semplice di poligono in cui si verifica la condizione suddetta.

    aberraz001

    E' un poligono avente 6 vertici di cui due coincidenti. Questo comporta che vi sono due lati del poligono perfettamente sovrapposti.

    Ma la condizione di coincidenza di due vertici non è necessaria, infatti potrebbe verificarsi il caso in cui pur non essendoci due vertici coincidenti si abbia comunque sovrapposizione tra i due lati convergenti in un vertice come per esempio:

    aberraz002

    o anche

    aberraz003n

    Infine potremmo anche avere due vertici coincidenti ma non avere sovrapposizione di lati
    aberraz004
    ma in quest'ultimo esempio la funzione non va in errore e procede nel suo compito.

    Infine ci possono essere casi con due lati sovrapposti o parzialmente sovrapposti ma che non afferiscono agli stessi vertici
    aberraz005

    Anche qui la funzione tacendo funziona dandoci un nuovo poligono che rispetta le condizioni di offset imposte e generando un poligono intrecciato.

    In sostanza l'errore si verifica solo e soltanto se vi è uno o piu vertici a cui afferiscono due lati sovrapposti anche solo parzialmente. Langolo formato tra questi lati è nullo ed il seno al denominatore provoca l'errore.

    Edited by afazio - 16/9/2012, 15:04
     
    Top
    .
  4.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Vediamo come si comporta autocad con l'offset del primo poligono che ho riportato

    ofauto001

    Intanto autocad non esegue alcun offset esterno (provare per credere) mentre esegue quello interno solo se l'entita dell'offset è minore della meta del lato piu piccolo del rettangolo ottenuto escludendo i due lati sovrapposti

    Il poligono offsettato ha perso praticamente due vertici e di conseguenza anche due lati.
     
    Top
    .
  5.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Mi aspetto un comportamento analogo nel caso in cui i due lati sovrapposti sono interni al poligono che resterebbe se togliessimo questi due lati, e cioè nessun offset interno e un offset esterno che mi ingrandisce il rettangolo.

    analogo001

    ma sono immediatamente smentito dal vile autocad che, è vero, non esegue offset interno, ma quando fa quello esterno mi restituisce un poligono (non intrecciatio) con due vertici in piu', e quindi due lati in più, rispetto a quello di partenza.

    analogo002

    Inizio a provare un leggero senso di disorientamento.
    Quale è il criterio adotatto da autocad nell'offset di poligoni?
     
    Top
    .
  6.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    COsa simile succede prendendo il secondo esempio:

    analogo003

    Nessun offset esterno, solo offset interno finquando il valore dell'offset è maggiore della metà del lato più piccolo del poligono restante dalla soppressione della parte sovrapposta dei due lati parzialmente sovrapposti. Quando poi l'offset supera questa metà allora a governare la possibilità dell'offset interno è la metà del piu piccolo dei lati rimanenti escludendo i precedenti per cui l'offset supera la loro metà. Questo finchè è possibile generare un poligono con almeno tre vertici, altrimenti non ne genera alcuno.

    analogo004

    Da sottolineare che in nessuno dei due casi Autocad esegue offset esterno.

    Se invece l'appendice delle sovrapposizioni è interno al poligono allora non viene eseguito alcun offset interno mentre viene eseguito quello esterno considerando il poligono che si ottiene depurandolo dalla parti sovrapposte
    ultimoq

    Ne deduco che autocad non genera mediante offset un poligono che si interseca col poligono originale e questo spiega il motivo per cui lo ha eseguito nel primo caso che ho riportato ma aumentando di due il numero dei vertici.

    LA cosa si fa complessa e per sperare di poterla districare occorrerebbe capire come si comporta autocad nel caso di offset dei poligoni intrecciati e già che ci siamo vedere come si comporta la nostra funzione. Fermorestando che potremmo comunque seguire un criterio diverso da quello di autocad.
    Riferirò

    Edited by afazio - 16/9/2012, 15:30
     
    Top
    .
  7. francesco.coppola
        +1   -1
     
    .

    User deleted


    Intanto che rifletto con afazio sui casi 1 e 2 che ha per primi presentato, e per i quali ho una idea ma voglio ancora rifletterci, approfitto per dire ai 'naviganti' che questo mio intervento è sostanzialmente basato sul nulla e di fatto potete saltarlo a piè pari.

    La programmazione difensiva
    Ogni volta che si scrive un pezzettino di codice, questo avrà una sua 'validità interna' (ovvero è un pezzo di codice che risolve un definito problema) ma ha anche la necessità di dati 'validi' in ingresso (ad esempio una funzione che estrae la radice quadrata andrà in crisi con un numero negativo).
    Ora per evitare che i nostri programmi abbia a bloccarsi ad ogni piè sospinto, bisogna 'validare' l'input in ingresso a queste funzioni.
    Tale 'validazione' potrà essere svolta a monte o all'interno della funzione. Quando il controllo di congruenza dei dati avviene all'interno della funzione, si potrebbe ricadere in quella che viene definita "programmazione difensiva".

    Secondo molti programmatori professionisti la "programmazione difensiva" genera più danni che guadagni, perchè di fatto, tendendo ad aggiustare i dati in ingresso ad una funzione nasconde gli errori, anzichè evidenziarli (e per almeno tentare di produrre codice senza errori, un errore che si evidenzia è sempre meglio di un errore che si nasconde).

    Ovviamente l'argomento è più ampio di come lo presento. Questo, ad esempio, un esempio tipico preso da un libro.
    Dovendo progettare il software che gestisce una centrale nucleare, dobbiamo gestire l'evento in cui il nocciolo del reattore aumenti inaspettatamente la sua temperatura interna. Che fare? Queste le alternative:
    1) Il software interviene aumentando la portata d'acqua di raffreddamento al reattore ed aspetta che la temperatura scenda;
    2) Il software lancia un allarme ed aspetta un intervento da parte dell'operatore umano che sta nella console di controllo;
    3) Il software aumenta la portata d'acqua di raffreddamento e contemporaneamente lancia un allarme.

    La prima sarebbe tipica della programmazione difensiva: "ci metto la pezza". E' ovvio che la temperatura nel nocciolo non debba salire, ma se questo accade ci sarà un motivo a monte, ed aumentare l'acqua di raffreddamento risolve oggi e forse anche domani, ma nasconde la vera causa dell'aumento di temperatura.
    Sulla seconda ipotesi non avrei nulla da ridire. Come un avvocato: "mi rimetto a voi signori della corte". Ma in qualche modo è sintomo di non aver fatto fino in fondo il proprio dovere di "bravi programmatori".
    L'autore del libro propende per l'ultima ipotesi.

    Torniamo più terra-terra alla nostra funzione di offset. Nel caso in cui ad un vertice arrivano due lati del poligono che formano un angolo=0, la funzione entra in crisi nel calcolo della variabile dist1.
    (il seno di 0 a denominatore fa generare un sonoro "division by zero").
    Che fare:
    Potremmo mettere un if, ovvero: "se il seno di alfa vale 0, allora......" allora cosa? In effetti se l'angolo vale zero il valore di dist1 perderebbe di significato, è giusto che esso tenda all'infinito, perchè due rette parallele è lì che vanno ad intercettarsi, pertanto qualsiasi fosse il valore alternativo che pensassimo per dist1 (magari dist1=dist) di fatto commettiamo un errore concettuale.
    E comunque in generale è assai difficile prevedere tutti i casi, e tutte le relative contromisure, in cui qualcosa potrebbe andare storto.
    Personalmente propendo a controllare l’input a monte, specie per funzioni che hanno ‘problemi digestivi’ se le cose non vanno come devono.
    Ricordiamoci che non sempre l’output di una funzione può essere graficizzato, e dunque accorgersi di qualcosa che non va. Anche questa stessa funzione genera un output che non necessariamente debba essere disegnato (ad esempio potrebbe essere utilizzata per definire il “volume staffa” in un algoritmo che intenda calcolare la duttilità di curvatura di una sezione confinata).
    Da questo punto di vista vedo dunque con favore la ‘voglia risolutiva’ di afazio di capire bene cosa succede nei casi che ha presentato e cosa fare almeno per i casi che balzano più agli occhi.
     
    Top
    .
  8.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Ed allora vediamo come Autocad tratta l'offset di poligono intrecciati. Dalle prove che ho fatto ho notato che NON ESEGUE MAI offset esterno (anche se vedremo che il concetto di interno-esterno per un poligono intrecciato perde di significato se non ne estendiamo diversamente la definizione), ma esegue solo offset interni (ad uno dei due poligono visibile ad occhio).
    Ecco l'esempio della farfallina:

    farfalla01

    La prima farfallina è perfettamente simmetrica (quindi avrebbe area nulla se calcolata con la formula di Gauss che ho trascritto in altro 3d)
    Per far disegnare i due triangolini interni, che come si vede sono semplicemente l'offset di altrettanti triangoli considerati singoli poligoni (ma io ho disegnato la farfalla sensa definire il punto di passaggio), occorre, dopo aver definito l'entità dell'offset far click su un lato e poi su un punto interno del primo triangolo e quindi ripetere l'operazione per il secondo triangolo.

    La seconda farfallina invece non è simmetrica, ottenuta dalla prima traslando uno dei lati obliqui.
    Anche qui Autocad non esegue offset esterno mentre per ottenere i due offset disegnati occorre procedere come sopra ma qui aggiungo che anche cliccando su un punto esterno ai due triangoli autocad esegue uno dei due offset ma sempre internamente. Non ho ben compreso con quale criterio esegue l'uno o l'altro offset ma penso che questo dipenda dalla posizione del punto dove ho fatto clik.

    Cosi notando che non riuscivo a far offset esterni ho disegnato tantisismi altri poligoni semplicemente intrecciati (cioè con un solo punto di intersezione al di fuori dei vertici). Confermo: nessun offset esterno.

    Edited by afazio - 16/9/2012, 19:03
     
    Top
    .
  9.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    A questo punto ho replicato la farfallina sul mio foglio di test ed ho provato a vedere cosa mi dava un offset interno, ottenendo prima questo:

    farfalla03

    poi, disabilitando il controllo dell'area nulla che avevo inserito a salvaguardia di un eventuale lista di vertici tutti allineati che mi danno appunto area nulla, questo

    farfalla04

    Che fare?
    Mi sembra che il risultato ottenuto sia accettabile ma bisogna vedere quale scopo ci prefiggiamo noi col nostro offset e se è possibile accettare come risultato quello che vediamo.
     
    Top
    .
  10.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Annotiamo il fatto che la nostra funzione ha proceduto rispettando tutto quanto stabilito nell'algoritmo discusso, annotiamo anche il fatto che il clock sarebbe anche indefinito (ed in questo caso il segno del flag non viene cambiato all'interno della funzione).
    Notiamo che mentre relativamente al primo triangolo l'offset sarebbe ad esso interno , diventa esterno relativamente al secondo triangolo ed infine che se volessimo come risultato un poligono non intrecciato dovremmo tagliare la seconda parte , cioè quella che or ora ho definito come esterna.
    Questo spiega perchè autocad non fa mai offset esterni nel caso di poligono intrecciati. Avranno scelto una strategia che nega la generazione di un poligono intrecciato da un'operazione di offset. MA questa potrebbe essere una necessità legata alla grafica ed al Cad mentre la nostra potrebbe anche differire.
    Tutto va bene purchè ne siamo consapevoli ed avvisiamo gli eventuali utenti della nostra funzione quali sono le caratteristiche della stessa.
    E poi c'è anche da dire che gli intrecci non hanno mai fine. Cosa dire se gli intrecci sono più di uno generando cosi regioni le cui aree potrebbero anche sovrapporsi tra loro?

    Ma il mio scopo non è quello di risolvere con unica funzione tutti i problemi legati all'offset anche perchè non ne sarei sinceramente in grado.
    Ed allora mi fermo qui, cercando solo di correggere tutti quegli errori che provocherebbero il crash del programma (tipo divisioni per sero, indici fuori intervallo ecc ecc) rinviando ad altre funzioni apposite basate su specifiche esigenze.

    Devo anche decidere se mantenere o meno l'attuale controllo di area nulla e cercare altro modo per verificare i punti allineati. Infine resta da risolvere la questione dei due lati sovrapposti (vediamo cosa ha pensato in merito sotto la doccia Francesco) e poi chiuderò dicendo: "questa funzione genera anche poligoni superintrecciati da poligoni semplici, intreccia l'intrecciato e farfalline e pescipalla a boccaperta e chiusa"
     
    Top
    .
  11. francesco.coppola
        +1   -1
     
    .

    User deleted


    E veniamo alla risoluzione del "problema del peduncolo".
    Sia esso 'maschio' o 'femmina', annuncio subito che la sua risoluzione è praticamente banale.

    Purtroppo mi contraddico subito, dicendo che pur parendo a prima vista banale, in effetti se si considera quanto 'male' possa disegnarsi un poligono la cosa è lievemente più complicata di quanto appaia a primo impatto.

    Ma andiamo a semplice primo impatto. Ecco un poligono con peduncolo:

    jpg

    Individuare un peduncolo è cosa assai semplice. Basterebbe determinare l'angolo tra due lati consecutivi del poligono. Se tale angolo vale 0 allora si è in presenza di un peduncolo (maschio come nel caso in figura, o femmina come potreste vedere in qualcuno degli schemi che afazio ha già postato).
    Invece di determinare l'angolo basta utilizzare la solita funzione che determina l'azimuth di un segmento orientato (la mia o quella di afazio pari sono). Se i due lati consecutivi del poligono (con polo nel vertice indagato) presentano azimuth uguali, ecco che si è in presenza di un peduncolo.

    Cosa fare per eliminarlo? Questa cosa è ancora più semplice di quella precedente.
    Bisogna eliminare il vertice in questione, e shiftare la numerazione dei vertici successivi di -1.
    L'effetto finale sarà il seguente:

    jpg

    E così via per tutti i rimanenti vertici.
    (Farete la prova anche con il peduncolo 'femmina' e vedrete che la soluzione è sempre quella: eliminare il vertice).

    Semplicissimo, vero?
    (E senza dover fare la doccia!).

    Bisogna però per forza fare la doccia nel momento in cui ci trovassimo di fronte ad un poligono come questo:

    jpg

    Ovvero un poligono con 'peduncolo doppio' (ma anche triplo, quadruplo, ecc.).
    In questo caso un algoritmo semplice semplice come quello che ho sopra descritto cadrebbe in difetto. Infatti esso vedrebbe il peduncolo nel vertice 4, opererebbe come abbiamo detto, eliminandolo e shiftando la numerazione e passerebbe al nuovo vertice 5 (ex 6), LASCIANDO PERò IL PEDUNCOLO NEL VERTICE 3.
    Altra cosa non da poco è che a questo punto il vertice 3 ed il vecchio vertice 5 (adesso 4) sono sovrapposti con conseguente sfracello nel calcolo dell'azimuth (quindi rendendo inservibile il criterio di controllo dell'azimuth per individuare il peduncolo).
    Pertanto una routine che consenta di risolvere il problema del peduncolo doppio-triplo, ecc. avrà anche come benefico effetto collaterale quello di eliminare i vertici sovrapposti.
    Il codice alla prossima.
     
    Top
    .
  12.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    CITAZIONE (francesco.coppola @ 17/9/2012, 16:43) 
    Cosa fare per eliminarlo? Questa cosa è ancora più semplice di quella precedente.
    Bisogna eliminare il vertice in questione, e shiftare la numerazione dei vertici successivi di -1.
    L'effetto finale sarà il seguente:

    jpg

    E così via per tutti i rimanenti vertici.
    (Farete la prova anche con il peduncolo 'femmina' e vedrete che la soluzione è sempre quella: eliminare il vertice).

    Semplicissimo, vero?
    (E senza dover fare la doccia!).

    Geniale!

    Lo dicevo io che forse era meglio che non fare la doccia.
     
    Top
    .
  13. francesco.coppola
        +1   -1
     
    .

    User deleted


    Inserisco adesso il codice, spero sufficientemente commentato (i testi racchiusi tra i simboli [/*] e [*/]).
    Come al solito ho adoperato le variabili globali polig (che sono in effetti una struttura dati, vedi qualche post precedente). Sarà afazio che le trasformerà nelle sue variabili ByRef (nel C il concetto è analogo, il riferimento alla variabile diventa il suo "puntatore").
    La funzione non soltanto quindi non accetta alcun input in ingresso (quella di afazio lo farà) ma nemmeno in uscita. Questo il motivo di quel void davanti il nome della funzione. La traduzione corretta di void dall'inglese sarebbe "indefinito", nella fattispecie significa nessuno.

    CODICE
    void elimina_peduncolo()
    {
    int register i,k;
    int km1,kp1;
    float azm1,azm2;

    /* Verifica - iterativamente - che primo ed ultimo vertice non coincidano */
    /* Se questo accade elimina l'ultimo vertice del poligono */
    while(polig.x[1]==polig.x[polig.numv] && polig.y[1]==polig.y[polig.numv])
         {
          polig.numv--;
         }

    for (k=1;k<=polig.numv;k++)
        {
         if (k==1) km1=polig.numv; else km1=k-1;
         if (k==polig.numv) kp1=1; else kp1=k+1;

         /* Controlla - iterativamente - che i vertici k e kp1 non coincidano */
         /* Se questo accade elimina il vertice kp1 e shifta i vertici successivi */
         while (polig.x[k]==polig.x[kp1] && polig.y[k]==polig.y[kp1])
              {
               for (i=kp1;i<=polig.numv-1;i++)
                   {
                    polig.x[i]=polig.x[i+1];
                    polig.y[i]=polig.y[i+1];
                   }
               polig.numv--;
              }
         /* A questo punto essendo certamente i segmenti k-km1 e k-kp1 non nulli, */
         /* Provvede a controllare la presenza del peduncolo */
         azm1=azimuth_segmento(polig.x[k],polig.y[k],polig.x[km1],polig.y[km1]);
         azm2=azimuth_segmento(polig.x[k],polig.y[k],polig.x[kp1],polig.y[kp1]);

         if (azm1==azm2) /* Vuol dire che siamo in presenza di un peduncolo */
            {
             for (i=k;i<=polig.numv-1;i++)
                 {
                  polig.x[i]=polig.x[i+1];
                  polig.y[i]=polig.y[i+1];
                 }
             polig.numv--;
             if (k!=1) k--;
             /* Con questa ultima istruzione si torna indietro nel poligono per */
             /* controllare se adesso in questo vertice precedente non si sia   */
             /* creato un nuovo peduncolo - caso peduncolo doppio, triplo, ecc. */
            }
        }
    }


    Ho fatto in modo che non si verificasse mai il caso di un segmento di lunghezza nulla. Ovvero di vertici consecutivi coincidenti.
    Poichè i vertici coincidenti potrebbero essere non solamente 2, ma 3, 4, 5, ....n (non c'è limite come dice afazio alle sciocchezze che possa fare l'utente di un qualsiasi programma), ho adoperato il comando while del C.
    Per capirlo bene dovrete tradurlo in italiano con: "fintanto che", ovvero le istruzioni tra parentesi graffa vengono eseguite fintando che la condizione posta subito dopo il while è vera.
    Questo taglia la testa al toro. Posso anche avere 100 vertici consecutivi uno sull'altro, verranno tutto eliminati.
    Lascio ad afazio la traduzione, ma anche il test di questa funzione, perchè io questa fase a dire il vero non l'ho eseguita.

    Infine due parole sulla eliminazione del "peduncolo". Qualcuno si potrebbe chiedere perchè modificare l'input dell'utente. Magari è una cosa voluta.
    In realtà il peduncolo porta solamente scompiglio, come in questo caso dell'offset, ma assolutamente nessun esito per qualsiasi altra cosa.
    Non modifica l'area del poligono, il suo baricentro, le inerzie, i momenti statici, ecc.
    Esso c'è o non c'è non cambia nulla. Quindi meglio toglierlo.
     
    Top
    .
  14.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Quindi questa procedura dovrebbe essere chiamata prima di chiamare la procedura per l'offset?
     
    Top
    .
  15. francesco.coppola
        +1   -1
     
    .

    User deleted


    Si, la procedura proposta dovrebbe essere chiamata prima.
    Magari insieme alla procedura che individui se il poligono è intrecciato o meno (se vogliamo eliminare il caso).
    Oppure insieme a procedure più banali, tipo: "il poligono è costituito da almeno 3 vertici?"

    Insomma una serie di funzioni che 'testino' la congruenza del poligono prima di farci qualsiasi altra cosa.
     
    Top
    .
61 replies since 13/9/2012, 12:54   2164 views
  Share  
.