Il Bar dell'Ingegneria

Smussiamo i poligoni

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

    Advanced Member

    Group
    Member
    Posts
    3,345
    Reputation
    +213

    Status
    Offline
    CITAZIONE (zax2013 @ 7/1/2014, 13:13) 
    Il problema è che non ho ancora capito bene quali siano le condizioni per stabilire senza indugio quali casi siano orari e quali antiorari. Devo mettermi lì a fare qualche ulteriore prova.

    se stabilisci che il verso di percorrenza del perimetro della figura è orario, allora il raccordo sarà percorso in senso orario (caso C-5) solo se il punto C è a sinistra del segmento di provenienza.

    per stabilire se il punto C è a sinistra del segmento di provenienza credo abbiate già a disposizione un algoritmo, ma ne ho creato uno anch'io postandolo da qualche parte in questo forum (era quel file excel che usava i determinanti di matrici 3x3 per trovare l'area dei triangoli. non ricordo in quale thread sia, ma afazio probabilmente sì).
     
    Top
    .
  2.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Member
    Posts
    2,939
    Reputation
    +187

    Status
    Offline
    Dopo qualche prova di troppo sono riuscito.
    Il 'trucco' stava nel calcolo corretto degli angoli, dati due azimuth.
    Domani preparo qualche schemino e pubblico il codice.
     
    Top
    .
  3.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Member
    Posts
    2,939
    Reputation
    +187

    Status
    Offline
    Sugli angoli tra segmenti

    Tutto il ragionamento fin qui svolto si basa su un approccio 'polare'.
    Ovvero abbiamo lavorato con azimuth di segmenti, con bisettrici, in generale dunque con angoli.
    Da questi, poi dobbiamo risalire alle coordinate cartesiane dei punti che compongono lo smusso.

    Ecco allora, che la funzione, già pubblicata, di determinazione dell'azimuth di un segmento ci è stata utilissima. Ma essa non basta quando dobbiamo valutare l'angolo tra due segmenti.

    Intanto abbiamo bisogno di una definizione: l'angolo formato dal segmento 1 con il segmento 2 è dato dall'angolo che il segmento 1 deve 'spazzare' quando, ruotando in senso orario, va ad 'adagiarsi' sul segmento 2.

    Da questa definizione le operazioni da svolgere sembrerebbero assai agevoli. Infatti, se azm2 è l'azimuth del segmento 2, e azm1 è l'azimuth del segmento 2, ecco che l'angolo tra il segmento 1 e 2 non possa che essere:

    alfa=azm2-amz1

    Guardiamo il seguente schema:

    png

    La formula sopra scritta è proprio il caso 1 dell'immagine.
    D'altra parte basta invertire il segmento 1 con il 2, per ottenere un angolo tra i due segmenti totalmente differente (Caso 2).
    Ma per come detto qualche post fa, per il nostro problema noi abbiamo bisogno sempre dell'angolo inferiore a 180° tra i due segmenti, ed eventualmente dotato anche di segno.
    Ovvero, per le elaborazioni che noi dobbiamo svolgere, non ci serve l'angolo +alfa, ma l'angolo -beta.

    Aggiungo ancora un ulteriore schema per far capire che le cose non sono mai 'semplici ' come ci si immagina a primo impatto:

    png

    Praticamente sia nella stessa situazione del caso 1 della figura precedente. Ma proviamo a mettere dei numeri e ad utilizzare la formula di cui sopra:

    azm1=6
    azm2=0.5
    alfa=0.5-6=-5.5

    Ma in base alla definizione l'angolo non può essere negativo, e nemmeno vale tanto (per la cronaca vale 0.78 e spiccioli).
    Ecco quindi che per gestire tutte queste situazioni 'particolari' (e per avere l'angolo che effettivamente mi serve per svolgere le successive elaborazioni) ho dovuto creare una funzione apposita:

    CODICE
    float angolo_tra(float azm1,float azm2)
    {
    float azm,pi;

    pi=4*atan(1);

    azm=azm2-azm1;

    if (azm>0 && azm>pi)  azm-=2*pi;
    if (azm<0 && azm<-pi) azm+=2*pi;

    return (azm);
    }


    Ovvero funzione che inizialmente applica la formula di cui sopra e che successivamente corregge l'angolo in modo che sia quello desiderato.
     
    Top
    .
  4.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Member
    Posts
    2,939
    Reputation
    +187

    Status
    Offline
    Cominciamo con il codice.
    Anzitutto la struttura dati 'minimale' che definisce il poligono:

    CODICE
    struct poligono_sezione
    {
    float x[100];
    float y[100];
    int numv;
    };


    Ovvero l'insieme di un vettore coordinate x e di un vettore coordinate y dei vertici che, nello stesso ordine dei vettori, via via uniti generano il poligono. Infine il numero totale dei vertici di cui il poligono è costituito.
    Per definizione il poligono è chiuso. Ovvero un segmento unisce sempre il primo vertice con l'ultimo.
    Ancora: il poligono 'gira' in senso orario, non presenta vertici doppioni, non presenta 'peduncoli', non presenta malcostituzioni di sorta.
    (Solito mio problema, al massimo il poligono potrà possedere 100 vertici).

    Prima della funzione vera e propria che riesce a 'smussare' i singoli vertici due funzioni di 'servizio'.

    CODICE
    float somma_azimuth(float azm1,float azm2)
    {
    float azm,pi;

    pi=4*atan(1);

    azm=azm1+azm2;

    /* La somma può essere algebrica (quindi anche sottrazione se uno dei due termini è negativo) */
    if (azm>2*pi) azm-=2*pi;
    if (azm<0.0) azm+=2*pi;

    return (azm);
    }


    Non fa altro che sommare due azimuth, restituendo, sempre, un valore compreso tra 0 e 6.28 rad. All'occorrenza il secondo parametro passato alla funzione potrebbe essere negativo, generando quindi una sottrazione tra gli azimuth e non una somma come 'letteralmente' farebbe pensare il nome della funzione. In realtà nella funzione principale alla fine non sfrutto mai tale evenienza.

    La seconda:

    CODICE
    /* -------------------------------------------------------------------------- */
    /* Scopo funzione   : Inserisce un numero 'n_vrt' di vertici subito dopo il   */
    /*                    vertice di numero 'index'                               */
    /* Parametri formali: polic=puntatore alla geometria della sezione            */
    /*                    index=vertice oltre il quale inserire n_vrt vertici     */
    /*                    n_vrt=numero di vertici da inserire                     */
    /* Valore restituito: struttura poligono_sezione in cui sono contenute le     */
    /*                    coordinate del poligono 'rinumerato'                    */
    /* Implementazione  : 20/12/2013                                              */
    /* Autore           : Zax2013                                                 */
    /* -------------------------------------------------------------------------- */
    struct poligono_sezione poli_insert_vertex(struct poligono_sezione polic,int index,int n_vrt)
    {
    int register k;

    polic.numv+=n_vrt;

    for (k=polic.numv-1-n_vrt;k>=index+1;k--)
        {
         polic.x[k+n_vrt]=polic.x[k];
         polic.y[k+n_vrt]=polic.y[k];
        }

    return (polic);
    }


    Questa non fa altro che 'traslare' i vertici del poligono successivi al vertice di numerazione index della quantità n_vrt.
    Questa funzione mi serve poichè al posto del singolo vertice l'operazione di smusso genererà 3-4-5-ecc. vertici, quindi i vertici successivi al vertice smussato cambieranno numerazione.
    Come vedete la funzione accetta in input una struttura dati poligono_sezione, e restituisce la stessa struttura con n_vrt vertici in più, avendo provveduto a 'creare' spazio subito dopo il vertice index. I vertici in più, quelli nuovi tra index e index+n_vrt rimangono con le stesse vecchie coordinate dei vertici index+1, index+2, ecc., in attesa che qualche altra funzione li utilizzi per definirne le coordinate effettive (la nostra funzione principale, dunque).

    Ecco infine la funzione vera e propria:


    CODICE
    struct poligono_sezione raccorda_vertici(struct poligono_sezione poli,int vrt,float r,int npunti)
    {
    int register k;
    int prec,succ;
    float azm1,azm2,azmb,alfa,azmini,azmfin,angradius,azmstep;
    float pi;
    float d1,t,l1,l2,xc,yc,xt1,yt1;

    /* Controlla la congruenza dei dati di input */
    if (r<=0 || npunti<=1)
       {
        /* Inserire qui eventuale avvertimento per l'utente */
        return (poli);
       }

    pi=4*atan(1);

    /* Definisce i vertici precedenti e successivi al vertice da smussare */
    prec=vrt-1; succ=vrt+1;

    if (prec<0) prec=poli.numv-1;
    if (succ>poli.numv-1) succ=0;

    /* Determina azimuth del lato precedente al vertice */
    azm1=azimuth_segmento(poli.x[vrt],poli.y[vrt],poli.x[prec],poli.y[prec]);
    /* Determina azimuth del lato successivo al vertice */
    azm2=azimuth_segmento(poli.x[vrt],poli.y[vrt],poli.x[succ],poli.y[succ]);

    /* Controlla per angolo piatto onde evitare incongruenze */
    if (fabs(azm2-azm1)==pi)
       {
        /* Inserire qui eventuale avvertimento per l'utente */
        return(poli);
       }
    /* Non controllo per angolo tra segmenti=0 poichè possibilità eliminata a monte */

    /* Determina azimuth bisettrice */
    azmb=(azm1+azm2)/2;
    if (fabs(azm2-azm1)>pi) azmb=somma_azimuth(azmb,pi);

    /* Angolo della bisettrice con uno dei segmenti */
    alfa=fabs(angolo_tra(azmb,azm2));
    d1=r/sin(alfa);
    t=r/tan(alfa);

    l1=sqrt(pow(poli.x[vrt]-poli.x[prec],2)+pow(poli.y[vrt]-poli.y[prec],2));
    l2=sqrt(pow(poli.x[vrt]-poli.x[succ],2)+pow(poli.y[vrt]-poli.y[succ],2));

    /* Controlla che la tangente non sia superiore alla lunghezza dei due segmenti da raccordare */
    if (t>l1 || t>l2)
       {
        /* Inserire qui eventuale avvertimento per l'utente */
        return(poli);
       }

    /* Controlla che il poligono non superi 100 vertici */
    if (poli.numv+npunti>99)
       {
        /* Inserire qui eventuale avvertimento per l'utente */
        return(poli);
       }

    /* A questo punto tutti i controlli hanno dato esito positivo, il raccordo è possibile */

    /* Coordinate centro del raccordo */
    xc=poli.x[vrt]+d1*sin(azmb);
    yc=poli.y[vrt]+d1*cos(azmb);

    /* Inserisce nel poligono i vertici necessari, a seguire vrt */
    poli=poli_insert_vertex(poli,vrt,npunti-1);

    /* Primo punto del raccordo su azm1 tangenza - sostituisce il vertice */
    xt1=poli.x[vrt]+t*sin(azm1);
    yt1=poli.y[vrt]+t*cos(azm1);
    /* Ultimo punto del raccordo su azm2 tangenza */
    poli.x[vrt+npunti-1]=poli.x[vrt]+t*sin(azm2);
    poli.y[vrt+npunti-1]=poli.y[vrt]+t*cos(azm2);

    poli.x[vrt]=xt1; poli.y[vrt]=yt1;

    azmini=azimuth_segmento(xc,yc,poli.x[vrt],poli.y[vrt]);
    azmfin=azimuth_segmento(xc,yc,poli.x[vrt+npunti-1],poli.y[vrt+npunti-1]);

    angradius=angolo_tra(azmini,azmfin);

    azmstep=angradius/(npunti-1);

    /* Determina le coordinate dei vertici seguenti vrt */
    for (k=1;k<=npunti-2;k++)
        {
         azmini=somma_azimuth(azmini,azmstep);

         poli.x[vrt+k]=xc+r*sin(azmini);
         poli.y[vrt+k]=yc+r*cos(azmini);
        }

    return(poli);
    }


    La funzione accetta in input una struttura dati poligono_sezione e restituisce lo stesso poligono ricevuto.
    All'occorrenza il poligono ricevuto viene restituito pari pari, se qualche 'evento' impedisce di poter procedere al raccordo.
    Questi eventi sono stati individuati essere:
    1) raggio di raccordo nullo;
    2) numero di suddivisioni del raccordo inferiore a 2 (vuol dire che nemmeno i punti di tangenza farebbero parte del raccordo);
    3) Angolo piatto tra vertice precedente-vertice-vertice successivo;
    4) Raggio di raccordo troppo elevato;
    5) NUmero di vertici finali superiori a 100.

    L'input della funzione è anche il numero del vertice da raccordare, il suo raggio di raccordo, ed in quanti vertici si vuol discretizzarlo.

    Per prima cosa vengono definiti i punti precedenti e successivi al vertice da smussare. E calcolati gli azimuth dei segmenti: vertice-punto precedente, vertice-punto successivo.
    Da questi si ricava l'azimuth della bisettrice, e i parametri geometrici per la determinazione del centro del raccordo.
    Quindi si calcolano le coordinate dei punti di tangenza del raccordo sul primo e sul secondo segmento (avendo provveduto a fare spazio nel poligono).
    Queste coordinate consentono di poter definire correttamente gli azimuth iniziali e finali dei vari raggi 'vettori' che via via consentono di definire i vari punti del raccordo.
    Infine con la funzione descritta nel post precedente si definisce correttamente l'angolo tra questi due azimuth (può essere sia positivo che negativo), e da questo lo step con cui il raggio vettore deve muoversi angolarmente per la definizione di ogni singolo punto del raccordo.
    Operazione questa che viene svolta dal ciclo for posto alla fine dalla funzione.
    Fine.
     
    Top
    .
  5.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    Alla prima occasione conto di tradurre in Pascal.
     
    Top
    .
  6.     +1   -1
     
    .
    Avatar

    Advanced Member

    Group
    Administrator
    Posts
    8,163
    Reputation
    +294

    Status
    Offline
    up
     
    Top
    .
35 replies since 30/12/2013, 23:17   1417 views
  Share  
.