Newsgroup: it.comp.lang.visual-basic
Titolo: Poliedri e def. di faccia

Ciao, dunque, per ragioni ridicole, mi son trovato a fare un 
programmino che a partire da uno sciame di punti dentro una 
sfera costruisce un poliedro
La logica opera attraverso la minizzazione di una energia 
totale (e locale) repulsiva, supponendo che i punti si 
respingano con legge quadratica inversa.
E' fin carino, per essere nato in 2-3 giorni (si possono 
disegnare solo i vertici, gli spigoli o entrambi, e ruotare 
sui tre assi cartesiani a velocità regolabili i poliedri).
Il divertente è vedere il poliedro rimaneggiarsi mentre si 
affina, anche se non sempre i solidi convergono a quelli 
euclidei regolari neppure quanto il numero di vertici lo 
permetterebbe. Vabbè, partendo da stati iniziali a caso, 
l'algoritmo resta invischiato in minimi di energia locali e 
non becca quello globale.
Ho messo dentro un obbrobbrioso (co' 4 B) algoritmo di 
ZORDER, che ragiona solo sui vertici e per questo è penoso.
Ora viene il casino. Stavo pensando a come cercare di fare 
dello Z-Order anche sulle facce, ma 1) per adesso il 
programma ragiona solo su vertici, e solo in maniera un po' 
grezza arriva a compilare una sorta di mappa di connessioni 
di ciascun vertice, 2) pensando a come definire 
operativamente una faccia (un poligono), mi sono accorto di 
non riuscire nemmeno a parole a trovare una definizione 
corretta. Ergo come potrei mai tentare di capire chi sta 
davanti, chi dietro, o se si intersecano due poligoni, se 
manco riesco a capire come si passa da una matrice di punti 
e da una mappa di connessioni, ad una lista di facce 
poligonali ?
Come si capisce ciò ?
Conosco solo la relazione di Eulero (tra vertici facce e 
spigoli), ma non so che farmene.
L'unica cosa, ovvia, che so dire per ora è : una faccia è un 
array circolare di vertici, ossia un percorso chiuso di 
vertici ... anzi forse il più breve percorso chiuso.
Ma ogni vertice appartiene a tante facce, indi la relazione 
è molti <-> molti.
Qualche idea di come rappresentare il concetto di FACCIA di 
un poliedro ?
ciao
grazie
Soviet_Mario

 Soviet_Mario

> Soviet_Mario

 
poï¿œï¿œlsta
Mmm
P.S. il programma che stai realizzando e' commerciale o abbiamo speranza 
di vederlo "in azione" ?
effettivamente e' un argomento piuttosto interessante

 di vederlo "in azione" ?
No, no, è uno sfizio.
Se il NG ha un suo sito dove poterlo postare (e se un 
qualsiasi utente, cosa che non credo, può postare) lo posso 
mandare.
Diversamente posso postare il codice pure qui sopra.
Sono, mi pare solo otto paginate per ora, è ridicolo. Cmq fa 
  già una parte del suo sporco lavoro :-)
Se interessa, fallo presente. Non ricordo se sia IT postare 
codice "completo" ... Se non è permesso/gradito, boh ...
ciao
Soviet


 "completo" ... Se non ï¿œ permesso/gradito, boh ...
il NG non dovrebbe accettare allegati binari e col VBxx e' quasi 
necessario averli
il sito comune c'e'!
http://www.it-lang-vb.net/
non so come si fa a "pubblicare" qualcosa, ma si fa!
su questi argomenti sono curioso come una scimmia, mi piacciono molto i 
programmi "matematici" e vorrei vederlo in azione

 programmi "matematici" e vorrei vederlo in azione
mi aggrego :-)

 "completo" ... Se non ï¿œ permesso/gradito, boh ...
Per inviare materiale al Sito Comune del NG ï¿œ sufficiente essere
registrati: 
http://www.it-lang-vb.net/registrazione.asp
Una volta registrato devi aspettare la conferma da parte mia (ï¿œ una
conferma manuale, quindi puï¿œ passare qualche ora).
La pagina di invio del materiale ï¿œ questa:
http://www.it-lang-vb.net/Invio_materiale.asp
Per una questione di sicurezza non sono piï¿œ accettati file binari ma
solo sorgenti.
Ciao,
Nicola

Soviet_Mario:
  no so' se l' informazione puo' esserti utile ma se il tuo problema
e' quello di trovare le facce di un poliedro definito da punti
sparpagliati e poi di disegnarlo nascondendo le parti coperte,
potresti usare un paio di "trucchetti" di cui, spesso, mi sono
servito in passato.
Per trovare le facce puoi calcolare il "convex-hull" definito dai
tuoi punti: le routines necessarie a questo calcolo le trovi nel
progetto Poliedri reperibile presso 
http://www.flanguasco.org
Attenzione: il convex-hull e' il minimo poliedro che contiene
tutti i punti dati per cui, se vi sono punti interni, questi NON
vengono visti; i poliedri regolari non soffrono di questa limitazione
(sono convessi).
Per disegnare il poliedro, nascondendo le facce coperte, il trucco
e' quello di disegnare le facce partendo da quelle piu' lontane
dall' osservatore: cosi' facendo le facce via, via disegnate
nasconderanno le parti coperte: un esempio di questa tecnica lo
puoi trovare nel progetto SurFit allo stesso indirizzo di cui sopra.
Ne ho anche qualche altra versione che, se vuoi, posso mandarti.
Io mi sono limitato a rotazioni intorno all' asse Z: se, come
mi sembra di capire, tu ruoti attorno ad assi qualsiasi, la cosa
dovrebbe essere ancora possibile, anche se piu' lenta.
Per il disegno ho sempre usato l' API Polygon, semplice da usare
e mooolto veloce.  Ah, dimenticavo, il tutto e' in VB6.
Ciao, spero di esserti stato utile e se ci fossero punti da
chiarire contattami pure.
Franco

 Franco

 associazione di idee)
Ciao, vado un po' OT quindi chiedo scusa,
se vogliamo parlare di nostalgia allora guarda un po' qui cosa ha fatto 
Andrea De Prisco
http://www.mc-online.it/
magari lo conosci giï¿œ ... ma se non fosse, ï¿œ un bel salto nel passato.
ciao
stefano

Soviet_Mario:
 > ...
 > Il tuo nome lo conosco, ma ï¿œ un ricordo tanto antico che purtroppo non
 > riesco piï¿œ a tracciarlo, accidenti ...
Probabilmente ricordi qualcuna delle mie risposte a questo NG: l' ho 
sempre frequentato, anche se saltuariamente, rispondendo, se ne ero in
grado, solo a domande riguardanti il calcolo scientifico.
 > ...
 >> Attenzione: il convex-hull e' il minimo poliedro che contiene
 >> tutti i punti dati per cui, se vi sono punti interni, questi NON
 >> vengono visti;
 >
 > visti nel senso di rappresentati ?
Mi sono espresso talmente male che mi auto-bacchetterei il dito con
cui scrivo :-))
"Non vengono visti" nel senso che la routine che calcola il convex-hull
ritorna solo i punti che definiscono, appunto, i vertici del 
convex-hull, ignorando quelli interni.
 > ...
 > Con le faccie poligonali non mi pare altrettanto banale, perchï¿œ non
 > identificate da un unico punto da confrontare in modo immediato
Io ho affrontato questo problema usando come punto da confrontare
il baricentro del poligono sotto esame.
E qui si apre un' interessante questione: le routines che ti ho 
segnalato ritornano, come facce esterne, solo i triangoli derivanti da 
una triangolazione di Delaunay.  C' e' una ragione, per questo: nello 
spazio 3D tre punti sono sicuramente complanari, quattro o piu'... non 
si sa', almeno nell' ambito della precisione finita del calcolatore.
Sicuro, potresti definire una quantita', piccola a piacere, per cui se 
un vertice dista dal piano definito da altri tre meno di essa questo 
viene considerato come appartenente alla stessa faccia ma... siamo 
proprio sicuri che questo sia vero e che, specialmente con punti 
sparpagliati quasi a caso, quest' ultimo vertice non pretenda di 
definire una faccia tutta per lui?
Prova, e.g., a definire un cubo (non, ovviamente, il caso di spigoli
orientati nella direzione degli assi e con vertici definiti da numeri 
interi) e calcola la distanza di un vertice qualsiasi dal piano definito 
dagli altri tre vertici della stessa faccia: moooolto difficilmente 
otterrai distanza zero!
 > Non ho ancora guardato nulla del codice ... ma solo vedere la mole di
 > roba tecnica che condividi sul tuo sito fa davvero impressione
Non impressionarti, il calcolo scientifico, fin dai primi programmi che 
ho scritto in FORTRAN (mio primo amore) all' inizio degi anni '70, e' 
sempre stato la mia passione che anche ora, da rottamato (leggi 
pensionato), continuo a coltivare, anche se i neuroni che gli posso 
dedicare diminuiscono ad un ritmo terrificante...
 > ...
 > Mi sa che magari darï¿œ un'occhio anche alle routines di calcolo
 > matriciale.
 > Averlo saputo prima, cavolo ... Mi ero scritto proprio ieri una
 > pidocchiosa routinetta per calcolare se un punto appartiene a un
 > piano, e ci ho messo un'ora solo a codificare un determinante 3x3 :-(
Se la cosa ti interessa e se mi fai, privatamente, avere il tuo
indirizzo E-Mail, posso inviarti il codice sorgente di due librerie di 
routines per il calcolo geometrico che in passato hanno risolto quasi 
ogni problema che ho dovuto affrontare: sono in FORTRAN ma le istruzioni 
di questo linguaggio sono facilmente traducibili, quasi uno a uno, in VB6.
Ciao, per ora ed a risentirci presto.
Franco

 di questo linguaggio sono facilmente traducibili, quasi uno a uno, in VB6.
Che dirti ... magari non mi servirà perché non programmo 
quasi più, ma se me la mandi mi fai un favore lo stesso e ti 
sono grato
ciao
Soviet_Mario


 stefano
ciao
grazie della segnalazione cmq :-)
tra un po' magari posto il codice di quel form dei poliedri 
... è un solo form, si sta dentro una decina di paginate
Soviet

Soviet_Mario:
 > ...
 >> nello spazio 3D tre punti sono sicuramente complanari, quattro o
 >> piu'... non si sa', almeno nell' ambito della precisione finita del
 >> calcolatore.
 > ...
 > Le facce di un unico punto non so bene cosa siano ... Un punto ï¿œ di
 > per sï¿œ stesso raccordato al resto tramite spigoli, per cui ï¿œ in comune
 > con almeno tre facce diverse ...
Altra bacchettata sul solito dito: mi sono espresso malissimo.
Scopo della mia sbrodolata di cui sopra, era quello di spiegare il
motivo per cui le routines del convex-hull ritornano una lista di
terne di vertici che individuano la superficie del poliedro usando SOLO
facce triangolari e non, e.g. facce quadrate per i cubi, pentagonali
per il dodecaedro o, comunque, poligonali.
Provo ad essere piu' chiaro: prendiamo il caso del solito cubo con
facce quadrate, 4 vertici per faccia, che possono essere (e vengono,
dalle routines, ritornate come) divise ognuna in due triangoli con
un lato comune.
Se di una faccia consideriamo tre vertici (primo triangolo) questi
sono sicuramente complanari ma come faccio a sapere se il quarto vertice
(appartenente al secondo triangolo ma non al primo) e' anche lui 
complanare e definisce pertanto, insieme agli altri tre, una faccia a 
quadrilatero?
Bene, ne misuro la distanza dal piano individuato dal primo triangolo,
distanza che sara' certamente molto piccola ma, quasi sicuramente, non 
nulla.
Se so' a priori che il poliedro e' sicuramente un cubo, la risposta e'
facile, piccola distanza = approssimazioni di calcolo = faccia a 
quadrilatero;
ma se sto' lavorando con punti sparpagliati (quasi) a caso il secondo
triangolo (che ha un lato in comune con il primo) potrebbe appartenere 
ad una faccia diversa, di pochissimo inclinata rispetto a quella
definita dal primo triangolo ma comunque distinta.
In quest' ultimo caso sarebbe errato considerare l' unione dei
due triangoli come un quadrilatero piano e disegnare, pertanto,
un' unica faccia quadrilatera.
Questo dovrebbe chiarire anche l' altra, infelice, mia frase:
 > ... che, specialmente con punti sparpagliati quasi
 >> a caso, quest' ultimo vertice non pretenda di definire una faccia
 >> tutta per lui?
 > E cos'ï¿œ una faccia degenerata ad un punto ?
Che, ovviamente, doveva essere scritta come:
"... non pretenda di definire una faccia tutta per lui [ed in comune
con gli altri due vertici del triangolo a cui appartiene]".
 > ...
 >>
 >> Se la cosa ti interessa e se mi fai, privatamente, avere il tuo
 >> indirizzo E-Mail,
 >
 > oh, posso mandarlo anche qui !
 >
 > G a t t o V i z z a t o @ g m a i l . c o m
Fatto, inviate subito prima di rispondere con questo messaggio.
Ciao, scusa ancora la mia fuorviante, prima risposta ed a presto.
Franco

 Franco

Poliedri generati elettrostaticamente
Ecco, sotto, il codice del programmino di cui parlavamo
La versione recente implementa un vero e proprio ordinamento 
ZORDER di tutti gli elementi grafici (vertici-palla, spigoli 
e raggi-diagonali), anche se possono essere visualizzati 
anche singolarmente e/o in qualsiasi combinazione.
Flickera un poco il redraw, ma non ricordo più come si 
evitasse il problema.
La rotazione (mouse drag, su tutte le viste in sezione, ma 
inattiva sulla orribile vista stereo, quella in 
verdegiallastro, senza prospettiva) è attiva anche DURANTE 
l'ottimizzazione, ma la visualizzazione va lenta e rallenta 
anche il calcolo. Per spicciarsi, girare di rado mentre 
ottimizza.
Ah, come si vede, lo ZORDER completo ha consentito una 
coloritura grafica più espressiva (i tratti in primo piano 
sono i più cupi e coprenti, e via via che si allontanano 
verso lo sfondo impallidiscono, rendendo possibile percepire 
alcuni "scavalcamenti" interni che in nero non si apprezzavano).
Cmq tutta fuffa :-) L'algoritmo di calcolo, un filo 
accelerato, è essenzialmente lo stesso di prima e non 
garantisce la generazione dei poliedri migliori in assoluto.
Dovrei introdurre una logica perturbativa parecchio più 
incasinata, come stato suggeritomo da F.Languasco e 
memorizzazione dei minimi storici trovati (a breve non ho 
voglia pensarci troppo alle strategie variazionali). Dovrei 
anzi ricompattare un po' tutto quanto prima di aggiungere 
features, che scricchiola e c'è qualche patch grossolana.
Gattaccio_Rognoso
Incollo sotto il testo dell'unico FORM del progetto.
Ah ... alcuni elementi in progettazione (Spero tutti !) 
ancora non usati sono stati commentati via, ma non ho voglia 
di tagliarli uno a uno. Forse è rimasto qualche tipo utente 
non ancora usato, portate pazienza (è innocuo). Basta 
incollare su un mono-form
''------------------------------------------------------------------------------------------------
VERSION 5.00
Begin VB.Form Frm_Poliedri
    Caption         =   "Frm_Poliedri"
    ClientHeight    =   6138
    ClientLeft      =   154
    ClientTop       =   671
    ClientWidth     =   5621
    LinkTopic       =   "Form1"
    ScaleHeight     =   6138
    ScaleWidth      =   5621
    StartUpPosition =   3  'Windows Default
    Begin VB.PictureBox pict_View_3D
       BackColor       =   &H00FFFFFF&
       ClipControls    =   0   'False
       Height          =   2552
       Left            =   2420
       ScaleHeight     =   125.4
       ScaleMode       =   0  'User
       ScaleWidth      =   113.3
       TabIndex        =   3
       TabStop         =   0   'False
       Top             =   2783
       Width           =   2310
    End
    Begin VB.CommandButton cmd_Go_Stop
       Caption         =   "GO"
       BeginProperty Font
          Name            =   "Courier New"
          Size            =   14.06
          Charset         =   0
          Weight          =   700
          Underline       =   0   'False
          Italic          =   0   'False
          Strikethrough   =   0   'False
       EndProperty
       Height          =   495
       Left            =   363
       TabIndex        =   4
       Top             =   5445
       Width           =   1221
    End
    Begin VB.PictureBox pict_View_X
       BackColor       =   &H00FFFFFF&
       ClipControls    =   0   'False
       Height          =   2552
       Left            =   0
       ScaleHeight     =   125.4
       ScaleMode       =   0  'User
       ScaleWidth      =   113.3
       TabIndex        =   2
       TabStop         =   0   'False
       Top             =   2783
       Width           =   2310
    End
    Begin VB.PictureBox pict_View_Y
       BackColor       =   &H00FFFFFF&
       ClipControls    =   0   'False
       Height          =   2552
       Left            =   2420
       ScaleHeight     =   125.4
       ScaleMode       =   0  'User
       ScaleWidth      =   113.3
       TabIndex        =   1
       TabStop         =   0   'False
       Top             =   121
       Width           =   2310
    End
    Begin VB.PictureBox pict_View_Z
       BackColor       =   &H00FFFFFF&
       ClipControls    =   0   'False
       Height          =   2552
       Left            =   0
       ScaleHeight     =   125.4
       ScaleMode       =   0  'User
       ScaleWidth      =   113.3
       TabIndex        =   0
       TabStop         =   0   'False
       Top             =   121
       Width           =   2310
    End
    Begin VB.Menu mPolyhedron
       Caption         =   "&Polyhedron"
       Begin VB.Menu mNumeroVertici
          Caption         =   "&Numero_Vertici ..."
          Shortcut        =   ^N
       End
       Begin VB.Menu mJointVertexThreshold
          Caption         =   "&JointVertexThreshold ..."
          Shortcut        =   ^J
       End
    End
    Begin VB.Menu mGraphics
       Caption         =   "Graphics"
       Begin VB.Menu mShowVertices
          Caption         =   "Show_&Vertices"
          Checked         =   -1  'True
          Shortcut        =   ^V
       End
       Begin VB.Menu mShowEdges
          Caption         =   "Show_&Edges"
          Checked         =   -1  'True
          Shortcut        =   ^E
       End
       Begin VB.Menu mShowRadii
          Caption         =   "Show_&Radii"
          Shortcut        =   ^R
       End
       Begin VB.Menu mExport
          Caption         =   "Ex&port"
          Begin VB.Menu mExportVerticesCoordinationNumbers
             Caption         =   "Vertices_&CoordinationNumbers"
             Shortcut        =   ^C
          End
          Begin VB.Menu mExportVerticesCoordinates
             Caption         =   "Vertices_C&oordinates"
             Shortcut        =   ^O
          End
       End
    End
End
Attribute VB_Name = "Frm_Poliedri"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Enum EnumAxis
     x_Axis = 10
     y_Axis = 20
     z_Axis = 30
End Enum '' EnumAxis
Private Enum EnumGraphicElemKind
     Radius = 4
     Edge = 8
     VertexSphere = 16
End Enum '' EnumAxis
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Type Type_Point_3D
     X As Double
     Y As Double
     Z As Double
End Type '' Type_Point_3D
Private Type Type_ZorderItem
     Kind As EnumGraphicElemKind
     MainCoordIdx As Integer
     SecondaryCoordIdx As Integer
End Type
Private Type Type_vector_3D
     DX As Double
     DY As Double
     DZ As Double
     '' Modulus As Double
End Type '' Type_vector_3D
Private Type Type_GraphMaxRange
     Min_X As Double
     Min_Y As Double
     Min_Z As Double
     Max_X As Double
     Max_y As Double
     Max_Z As Double
End Type '' Type_GraphMaxRange
Private Type Type_VertexConnections
     HasSomeJoints As Boolean
     JointedIdxes() As Integer
End Type '' Type_VertexConnections
'' SUPER MEGA CONTENITORE INCAPSULANTE, coi GLOBALONI
Private Type Type_Glo
     Pts() As Type_Point_3D
     ZOrderIndexes() As Type_ZorderItem
     ForcesResults() As Type_vector_3D
     Connections() As Type_VertexConnections
     Ranges As Type_GraphMaxRange
     Baric As Type_Point_3D
     CurrRotationAxis As EnumAxis
     MotionExtent As Double
     NumeroVertici As Integer
     NumeroSpigoli As Integer
     RotazAngle As Double
     PercentDistanceSogliaJointed As Double
End Type '' Type_Glo
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Const ConstUnoSuRadiceDue As Double = 0.707106781186548 ' 
0,70710678118654752440084436210485
Const ConstPIGreco As Double = 3.14159265358979 ' 
3.1415926535897932384626433832795
Const ConstDuePIGreco As Double = 6.28318530717959 ' 
6,283185307179586476925286766559
Const ConstQuattroPIGreco As Double = 12.5663706143592 ' 
12.566370614359172953850573533118
Const ConstDefaultRotazAngle As Double = ConstPIGreco * 0.01
Const ConstDefaultNumVertici As Integer = 12
Const ConstMaxNumVertici As Integer = 512
Const ConstForceUnitary As Double = 1#
''Const ConstMotionExtent As Double = 1#
Const ConstMotionExtentSlowingRate As Double = 0.95
Const ConstMinimalMotionRagPercent As Double = 0.05
Const ConstMaxGraphRange As Double = 1E+300
''Const ConstMaxRepulsiveForce As Double = (1# / 12#)
Const ConstDefaultInitialSphereRadius As Double = 1#
Const ConstGraphZoomMargin As Double = 1.05
Const ConstGraphCirclePercentRadius As Double = 0.35
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
'' SUPER MEGA GLOBALONE incapsulante
Private Glo As Type_Glo
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Function FzCountNONSimplifiedEdges(ByRef Conn() As 
Type_VertexConnections) As Integer
Dim J As Integer
Dim TotEdges As Integer
TotEdges = 0
For J = LBound(Conn()) To UBound(Conn())
     With Conn(J)
         If (.HasSomeJoints = True) Then
             TotEdges = TotEdges + (UBound(.JointedIdxes()) 
- LBound(.JointedIdxes()) + 1)
         End If
     End With
Next J
FzCountNONSimplifiedEdges = TotEdges
End Function
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub 
FzGenerateAddEdgesListFromConnectedVertices(ByRef Glo As 
Type_Glo)
Dim J As Integer
Dim Q As Integer
Dim TotEdgesRaw As Integer
Dim LastAddedGraphObj As Integer
Dim TmpZOrderEdges() As Type_ZorderItem
With Glo
     TotEdgesRaw = FzCountNONSimplifiedEdges(.Connections())
     ReDim TmpZOrderEdges(0 To TotEdgesRaw - 1) As 
Type_ZorderItem
     TotEdgesRaw = -1
     '' compila tutti i lati, senza considerare che sono 
DOPPIONI
     For J = LBound(.Connections()) To UBound(.Connections())
         If (.Connections(J).HasSomeJoints = True) Then
             For Q = LBound(.Connections(J).JointedIdxes()) 
To UBound(.Connections(J).JointedIdxes())
                 TotEdgesRaw = TotEdgesRaw + 1
                 TmpZOrderEdges(TotEdgesRaw).Kind = Edge
                 TmpZOrderEdges(TotEdgesRaw).MainCoordIdx = J
 
TmpZOrderEdges(TotEdgesRaw).SecondaryCoordIdx = 
.Connections(J).JointedIdxes(Q)
             Next Q
         End If
     Next J
     '' rimuove tutti i doppioni
     TotEdgesRaw = UBound(TmpZOrderEdges())
     For J = 0 To UBound(TmpZOrderEdges()) - 1
         For Q = TotEdgesRaw To (J + 1) Step -1
             If ((TmpZOrderEdges(J).MainCoordIdx = 
TmpZOrderEdges(Q).SecondaryCoordIdx) And 
(TmpZOrderEdges(J).SecondaryCoordIdx = 
TmpZOrderEdges(Q).MainCoordIdx)) Then
                 If Not (Q = TotEdgesRaw) Then
                     TmpZOrderEdges(Q) = 
TmpZOrderEdges(TotEdgesRaw)
                 End If
                 TotEdgesRaw = TotEdgesRaw - 1
             End If
         Next Q
     Next J
     '' importante per gli utenti esterni : aggiorna il flag 
GLOBALE
     .NumeroSpigoli = TotEdgesRaw
     '' trancia via gli spigoli duplicati
     ReDim Preserve TmpZOrderEdges(0 To TotEdgesRaw) As 
Type_ZorderItem
     '' ridimensiona l'array comune di tutti gli oggetti 
grafici da ZORDERARE altrove
     LastAddedGraphObj = UBound(.ZOrderIndexes()) + 1
     ReDim Preserve .ZOrderIndexes(LBound(.ZOrderIndexes()) 
To (LastAddedGraphObj + TotEdgesRaw)) As Type_ZorderItem
     '' accoda gli spigoli (EDGES) all'array comune di tutti 
gli oggetti grafici da ZORDERARE altrove
     For J = 0 To UBound(TmpZOrderEdges())
         .ZOrderIndexes(LastAddedGraphObj + J) = 
TmpZOrderEdges(J)
     Next J
End With
Erase TmpZOrderEdges()
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzRebuildAllGraphicsObjectsList(ByRef Glo As 
Type_Glo, ByVal RequiredTypesMask As Long)
Dim J As Integer
Dim NumObj As Integer
NumObj = 0
If (RequiredTypesMask And EnumGraphicElemKind.VertexSphere) Then
     NumObj = NumObj + Glo.NumeroVertici
End If
If (RequiredTypesMask And EnumGraphicElemKind.Radius) Then
     NumObj = NumObj + Glo.NumeroVertici
End If
ReDim Glo.ZOrderIndexes(0 To NumObj) As Type_ZorderItem
NumObj = -1
If (RequiredTypesMask And EnumGraphicElemKind.VertexSphere) Then
     For J = LBound(Glo.Pts()) To UBound(Glo.Pts())
         NumObj = NumObj + 1
         With Glo.ZOrderIndexes(NumObj)
             .Kind = VertexSphere
             .MainCoordIdx = J
             .SecondaryCoordIdx = -1
         End With
     Next J
End If
If (RequiredTypesMask And EnumGraphicElemKind.Radius) Then
     For J = LBound(Glo.Pts()) To UBound(Glo.Pts())
         NumObj = NumObj + 1
         With Glo.ZOrderIndexes(NumObj)
             .Kind = Radius
             .MainCoordIdx = J
             .SecondaryCoordIdx = -1
         End With
     Next J
End If
If (RequiredTypesMask And EnumGraphicElemKind.Edge) Then
     Call FzGenerateAddEdgesListFromConnectedVertices(Glo)
End If
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Function FzGetZorderItemValue(ByRef ZOrderItem As 
Type_ZorderItem, ByRef Coords() As Type_Point_3D, ByVal Asse 
As EnumAxis) As Double
With ZOrderItem
     Select Case .Kind
     Case EnumGraphicElemKind.VertexSphere
         Select Case Asse
         Case EnumAxis.x_Axis
             FzGetZorderItemValue = Coords(.MainCoordIdx).X
         Case EnumAxis.y_Axis
             FzGetZorderItemValue = Coords(.MainCoordIdx).Y
         Case EnumAxis.z_Axis
             FzGetZorderItemValue = Coords(.MainCoordIdx).Z
         End Select
     Case EnumGraphicElemKind.Radius
         Select Case Asse
         Case EnumAxis.x_Axis
             FzGetZorderItemValue = Coords(.MainCoordIdx).X 
* 0.5
         Case EnumAxis.y_Axis
             FzGetZorderItemValue = Coords(.MainCoordIdx).Y 
* 0.5
         Case EnumAxis.z_Axis
             FzGetZorderItemValue = Coords(.MainCoordIdx).Z 
* 0.5
         End Select
     Case EnumGraphicElemKind.Edge
         Select Case Asse
         Case EnumAxis.x_Axis
             FzGetZorderItemValue = (Coords(.MainCoordIdx).X 
+ Coords(.SecondaryCoordIdx).X) * 0.5
         Case EnumAxis.y_Axis
             FzGetZorderItemValue = (Coords(.MainCoordIdx).Y 
+ Coords(.SecondaryCoordIdx).Y) * 0.5
         Case EnumAxis.z_Axis
             FzGetZorderItemValue = (Coords(.MainCoordIdx).Z 
+ Coords(.SecondaryCoordIdx).Z) * 0.5
         End Select
     End Select
End With
End Function
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
'' Iterative QuickSort
Private Sub FzSortArrayZOrder(ByRef Pts() As Type_Point_3D, 
ByRef ZOrderIndexes() As Type_ZorderItem, ByVal RotationAxis 
As EnumAxis)
Dim TmpSwap As Type_ZorderItem
Dim IdxRnd As Integer
Dim FirstIdx As Integer
Dim LastIdx As Integer
Dim LowIdx As Integer
Dim HighIdx As Integer
Dim TopOfStack As Integer
Dim Stack() As Integer
FirstIdx = LBound(ZOrderIndexes())
LastIdx = UBound(ZOrderIndexes())
ReDim Stack(0 To (LastIdx - FirstIdx + 1)) As Integer
TopOfStack = -1
'' scanning
Do
     Do '' Swap from ends until first and last meet in the 
middle
         If (FirstIdx < LastIdx) Then '' If we’re in the 
middle and out of order, swap
             If ((LastIdx - FirstIdx) = 1) Then
                 If 
(FzGetZorderItemValue(ZOrderIndexes(FirstIdx), Pts(), 
RotationAxis) > FzGetZorderItemValue(ZOrderIndexes(LastIdx), 
Pts(), RotationAxis)) Then
                     TmpSwap = ZOrderIndexes(FirstIdx)
                     ZOrderIndexes(FirstIdx) = 
ZOrderIndexes(LastIdx)
                     ZOrderIndexes(LastIdx) = TmpSwap
                 End If
             Else
                 '' Split at some random point
                 '' .Swap Pts(LastIdx), 
Pts(MRandom.Random(FirstIdx, LastIdx))
                 IdxRnd = FirstIdx + Rnd * (LastIdx - FirstIdx)
                 TmpSwap = ZOrderIndexes(FirstIdx)
                 ZOrderIndexes(FirstIdx) = ZOrderIndexes(IdxRnd)
                 ZOrderIndexes(IdxRnd) = TmpSwap
                 '' Swap high values below the split for low 
values above
                 LowIdx = FirstIdx
                 HighIdx = LastIdx
                 Do
                     '' Find any low value larger than split
                     Do While (LowIdx < HighIdx)
                         If 
(FzGetZorderItemValue(ZOrderIndexes(LowIdx), Pts(), 
RotationAxis) > FzGetZorderItemValue(ZOrderIndexes(LastIdx), 
Pts(), RotationAxis)) Then
                             Exit Do
                         End If
                         LowIdx = LowIdx + 1
                     Loop
                     '' Find any high value smaller than split
                     Do While (HighIdx > LowIdx)
                         If 
(FzGetZorderItemValue(ZOrderIndexes(HighIdx), Pts(), 
RotationAxis) < FzGetZorderItemValue(ZOrderIndexes(LastIdx), 
Pts(), RotationAxis)) Then
                             Exit Do
                         End If
                         HighIdx = HighIdx - 1
                     Loop
                     '' Swap too high low value for too low 
high value
                     If (LowIdx < HighIdx) Then
                         TmpSwap = ZOrderIndexes(LowIdx)
                         ZOrderIndexes(LowIdx) = 
ZOrderIndexes(HighIdx)
                         ZOrderIndexes(HighIdx) = TmpSwap
                     End If
                 Loop While (LowIdx < HighIdx)
                 '' Current (LowIdx) is larger than split 
(LastIdx), so swap
                 TmpSwap = ZOrderIndexes(LowIdx)
                 ZOrderIndexes(LowIdx) = ZOrderIndexes(LastIdx)
                 ZOrderIndexes(LastIdx) = TmpSwap
                 '' Push range markers of larger part for 
later sorting
                 If ((LowIdx - FirstIdx) < (LastIdx - 
LowIdx)) Then
                     TopOfStack = TopOfStack + 1
                     GoSub Check__Stack
                     Stack(TopOfStack) = LowIdx + 1
                     TopOfStack = TopOfStack + 1
                     GoSub Check__Stack
                     Stack(TopOfStack) = LastIdx
                     LastIdx = LowIdx - 1
                 Else
                     TopOfStack = TopOfStack + 1
                     GoSub Check__Stack
                     Stack(TopOfStack) = FirstIdx
                     TopOfStack = TopOfStack + 1
                     GoSub Check__Stack
                     Stack(TopOfStack) = LowIdx - 1
                     FirstIdx = LowIdx + 1
                 End If
                 '' Exit from inner loop to process smaller part
                 Exit Do
             End If
         End If
         '' stack vuoto, esce dal ciclo esterno
         If (TopOfStack < 0) Then
             Erase Stack '' non serve più
             Exit Sub
         End If
         '' stack non vuoto : recupera FirstIdx e LastIdx 
dall'ultima sezione auto-delegata
         LastIdx = Stack(TopOfStack)
         TopOfStack = TopOfStack - 1
         If (TopOfStack < 0) Then
             Err.Raise 33333, "FzSortArrayZOrder", "Internal 
Stack Problems while Sorting"
         End If
         FirstIdx = Stack(TopOfStack)
         TopOfStack = TopOfStack - 1
     Loop
Loop
Exit Sub
'' SUBROUTINE interna di stack autonomo : iteratizza la 
ricorsione degli estremi dell'array
Check__Stack:
     If (TopOfStack > UBound(Stack())) Then
         ReDim Preserve Stack(LBound(Stack()) To (TopOfStack 
+ 50)) As Integer
     End If
Return
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzSortArray_ZOrder(ByRef Glo As Type_Glo)
Call FzSortArrayZOrder(Glo.Pts(), Glo.ZOrderIndexes(), 
Glo.CurrRotationAxis)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Function InputValue(ByVal Prompt As String, ByVal 
Title As String, ByVal Default As String) As Double
Dim Tmp As String
Dim Virgola As Integer
Tmp = InputBox(Prompt, Title, Default)
If (Tmp = "") Then
     InputValue = 0#
     Exit Function
End If
Virgola = InStr(Tmp, ",")
If (Virgola > 0) Then
     Mid$(Tmp, Virgola, 1) = "." '' mette il punto
End If
InputValue = Val(Tmp)
End Function
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzResultantRepulsiveForceOneVertex(ByRef Glo As 
Type_Glo, ByVal RefIdx As Integer)
Dim J As Integer
Dim Modulus As Double
Dim Denom As Double
Dim X_D As Double, Y_D As Double, Z_D As Double
'' dato UN punto dato, calcola la forza su esso esercitata 
da TUTTI gli altri
With Glo.ForcesResults(RefIdx)
     .DX = 0#
     .DY = 0#
     .DZ = 0#
End With
With Glo.Pts(RefIdx)
     For J = LBound(Glo.Pts()) To UBound(Glo.Pts())
         If Not (J = RefIdx) Then '' salta il punto stesso 
(SELF)
             '' Modulus = ConstForceUnitary / 
FzFastDistanzaSemiCubica(Glo.Pts(J), Glo.Pts(RefIdx))
             X_D = (.X - Glo.Pts(J).X)
             Y_D = (.Y - Glo.Pts(J).Y)
             Z_D = (.Z - Glo.Pts(J).Z)
             '' Modulus = ConstForceUnitary / (((X_D * X_D) 
+ (Y_D * Y_D) + (Z_D * Z_D)) ^ (1.5)) '' distanza semicubica
             Denom = (((X_D * X_D) + (Y_D * Y_D) + (Z_D * 
Z_D)) ^ (5#))
             If Not (Denom = 0#) Then
                 Modulus = ConstForceUnitary / (((X_D * X_D) 
+ (Y_D * Y_D) + (Z_D * Z_D)) ^ (2.5))  '' distanza semicubica
             Else
                 Modulus = ConstForceUnitary / 0.000000001 
'' distanza semicubica
             End If
             With Glo.ForcesResults(RefIdx)
                 .DX = .DX + Modulus * X_D
                 .DY = .DY + Modulus * Y_D
                 .DZ = .DZ + Modulus * Z_D
             End With
         End If
     Next J
End With
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzCalcALLResultingForcesAllVertices(ByRef Glo As 
Type_Glo)
Dim CurrIdx As Integer
ReDim Glo.ForcesResults(LBound(Glo.Pts()) To 
UBound(Glo.Pts())) As Type_vector_3D
For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
     Call FzResultantRepulsiveForceOneVertex(Glo, CurrIdx)
Next CurrIdx
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzRotateAllVertices(ByRef Glo As Type_Glo)
Dim CurrIdx As Integer
Dim SinAng As Double
Dim CosAng As Double
Dim Old_X As Double, Old_Y As Double, Old_Z As Double
SinAng = Sin(Glo.RotazAngle)
CosAng = Cos(Glo.RotazAngle)
'' aggiorna posizioni
Select Case Glo.CurrRotationAxis
Case EnumAxis.x_Axis
     For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
         With Glo.Pts(CurrIdx)
             Old_Y = .Y
             Old_Z = .Z
             .Y = Old_Y * CosAng - Old_Z * SinAng
             .Z = Old_Y * SinAng + Old_Z * CosAng
         End With
     Next CurrIdx
Case EnumAxis.y_Axis
     For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
         With Glo.Pts(CurrIdx)
             Old_X = .X
             Old_Z = .Z
             .X = Old_X * CosAng - Old_Z * SinAng
             .Z = Old_X * SinAng + Old_Z * CosAng
         End With
     Next CurrIdx
Case EnumAxis.z_Axis
     For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
         With Glo.Pts(CurrIdx)
             Old_X = .X
             Old_Y = .Y
             .X = Old_X * CosAng - Old_Y * SinAng
             .Y = Old_X * SinAng + Old_Y * CosAng
         End With
     Next CurrIdx
End Select
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzMoveAllVertices(ByRef Glo As Type_Glo)
Dim CurrIdx As Integer
Dim MotionModulus As Double
Dim Distance As Double
Dim Pt As Type_Point_3D
With Glo.Ranges
     .Max_X = -ConstMaxGraphRange
     .Max_y = -ConstMaxGraphRange
     .Max_Z = -ConstMaxGraphRange
     .Min_X = ConstMaxGraphRange
     .Min_Y = ConstMaxGraphRange
     .Min_Z = ConstMaxGraphRange
End With
'' delega i pesanti calcoli
Call FzCalcALLResultingForcesAllVertices(Glo)
MotionModulus = Glo.MotionExtent '' DefaultConstMotionExtent 
'' * ForcesResults(CurrIdx).Modulus
'' aggiorna posizioni
For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
     Pt = Glo.Pts(CurrIdx)
     With Glo.ForcesResults(CurrIdx)
         '' sposta liberamente nello spazio, COME SE NON CI 
FOSSE LA SFERA DI CONFINE ESTERNO
         Pt.X = Pt.X + MotionModulus * .DX
         Pt.Y = Pt.Y + MotionModulus * .DY
         Pt.Z = Pt.Z + MotionModulus * .DZ
     End With
     '' collapses back to the sphere (proietta RADIALMENTE 
indietro riportando
     '' il punto SULLA sfera : ma gli aggiustamenti 
tangenziali restano intatti)
     With Pt
         Distance = 1# / (Sqr((.X * .X) + (.Y * .Y) + (.Z * 
.Z)))
         '' calcolato fattore di scaling proporzionale
         .X = .X * Distance
         .Y = .Y * Distance
         .Z = .Z * Distance
     End With
     '' ricopia
     Glo.Pts(CurrIdx) = Pt
     With Glo.Ranges
         '' aggiorna i ranges, per scalare la window grafica
         If (Pt.X > .Max_X) Then .Max_X = Pt.X
         If (Pt.X < .Min_X) Then .Min_X = Pt.X
         If (Pt.Y > .Max_y) Then .Max_y = Pt.Y
         If (Pt.Y < .Min_Y) Then .Min_Y = Pt.Y
         If (Pt.Z > .Max_Z) Then .Max_Z = Pt.Z
         If (Pt.Z < .Min_Z) Then .Min_Z = Pt.Z
     End With
Next CurrIdx
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzCalculateBaricentro(ByRef Glo As Type_Glo)
Call FzCalcBaricentro(Glo.Pts(), Glo.Baric)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzCalcBaricentro(ByRef Pts() As Type_Point_3D, 
ByRef Baric As Type_Point_3D)
Dim J As Integer
Dim NumPunti As Integer
NumPunti = (UBound(Pts()) - LBound(Pts()) + 1)
With Baric
     .X = 0#
     .Y = 0#
     .Z = 0#
     For J = LBound(Pts()) To UBound(Pts())
         .X = .X + Pts(J).X
         .Y = .Y + Pts(J).Y
         .Z = .Z + Pts(J).Z
     Next J
     .X = .X / NumPunti
     .Y = .Y / NumPunti
     .Z = .Z / NumPunti
End With
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub InitPoints(ByRef Glo As Type_Glo, Optional ByVal 
DefaultInitialSphereRadius As Double = 
ConstDefaultInitialSphereRadius)
Dim CurrIdx As Integer
Dim SphereSurface As Double
Dim EachSlice As Double
Dim AverageDistance As Double
ReDim Glo.Pts(0 To Glo.NumeroVertici - 1) As Type_Point_3D
With Glo.Ranges
     .Max_X = -ConstMaxGraphRange
     .Max_y = -ConstMaxGraphRange
     .Max_Z = -ConstMaxGraphRange
     .Min_X = ConstMaxGraphRange
     .Min_Y = ConstMaxGraphRange
     .Min_Z = ConstMaxGraphRange
End With
'' inizializza a casaccio tutti i punti, all'interno
'' di una SFERA di raggio UNITARIO
For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
     With Glo.Pts(CurrIdx)
         .X = Sgn(0.5 - Rnd()) * Rnd() * 
DefaultInitialSphereRadius
         .Y = Sgn(0.5 - Rnd()) * Rnd() * 
DefaultInitialSphereRadius
         .Z = Sgn(0.5 - Rnd()) * Rnd() * 
DefaultInitialSphereRadius
     End With
Next CurrIdx
'' centra nell'origine
Dim Baric As Type_Point_3D
Call FzCalculateBaricentro(Glo)
'' mette il centro nell'ORIGINE della sfera iniziale
'' aggiorna anche i ranges per lo scaling delle graphic windows
For CurrIdx = LBound(Glo.Pts()) To UBound(Glo.Pts())
     With Glo.Pts(CurrIdx)
         .X = .X - Baric.X
         If (.X > Glo.Ranges.Max_X) Then Glo.Ranges.Max_X = .X
         If (.X < Glo.Ranges.Min_X) Then Glo.Ranges.Min_X = .X
         .Y = .Y - Baric.Y
         If (.Y > Glo.Ranges.Max_y) Then Glo.Ranges.Max_y = .Y
         If (.Y < Glo.Ranges.Min_Y) Then Glo.Ranges.Min_Y = .Y
         .Z = .Z - Baric.Z
         If (.Z > Glo.Ranges.Max_Z) Then Glo.Ranges.Max_Z = .Z
         If (.Z < Glo.Ranges.Min_Z) Then Glo.Ranges.Min_Z = .Z
     End With
Next CurrIdx
'' dimensiona il livello iniziale degli spostamenti laterali
'' basandosi sul ravvicinamento medio dei punti (assunti 
sulla superficie)
SphereSurface = ConstQuattroPIGreco ''' * 1#^2 '' (il raggio 
è unitario)
EachSlice = SphereSurface / Glo.NumeroVertici '' area media 
pro-vertex
AverageDistance = Sqr(EachSlice / ConstPIGreco) '' raggio 
della suddetta
Glo.PercentDistanceSogliaJointed = Sqr(2#) * 0.99
'' GloPercentDistanceSogliaJointed = 1.33333333333333 
'' un terzo % in più della distanza minima osservata
Glo.MotionExtent = 0.1 * AverageDistance '' distanza media 
tra due vertici già ottimizzati
'' N.B. : uso di globaloni del cazzo, occhio alle dipendenze 
esterne di questa routine
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub FzDesumeConnections(ByRef Glo As Type_Glo)
Dim J As Integer
Dim Q As Integer
Dim Top As Integer
Dim CurrDistanza As Double
Dim MinDistanza As Double
Dim AverageDistanza As Double
Dim DistanzaSoglia As Double
Dim PtJ As Type_Point_3D
MinDistanza = 1E+300
AverageDistanza = 0#
'' usa il quadrato nei confronti, per sbrigarsi !
For J = LBound(Glo.Pts()) To UBound(Glo.Pts())
     PtJ = Glo.Pts(J)
     For Q = LBound(Glo.Pts()) To UBound(Glo.Pts())
         With Glo.Pts(Q)
             If Not (Q = J) Then
                 CurrDistanza = ((PtJ.X - .X) * (PtJ.X - 
.X)) + ((PtJ.Y - .Y) * (PtJ.Y - .Y)) + ((PtJ.Z - .Z) * 
(PtJ.Z - .Z))
                 If (MinDistanza > CurrDistanza) Then 
MinDistanza = CurrDistanza
                 AverageDistanza = AverageDistanza + 
CurrDistanza
             End If
         End With
     Next Q
Next J
'' adesso, del quadrato minore, estrae la radice (tricky)
'' MinDistanza = Sqr(MinDistanza)
'' AverageDistanza = Sqr(AverageDistanza / 
CDbl(UBound(Glo.Pts()) - LBound(Glo.Pts()) + 1))
AverageDistanza = AverageDistanza / CDbl(UBound(Glo.Pts()) - 
LBound(Glo.Pts()) + 1)
DistanzaSoglia = MinDistanza * 
Glo.PercentDistanceSogliaJointed * 
Glo.PercentDistanceSogliaJointed '' 1.25 '' + 
(AverageDistanza - MinDistanza) ^ 0.5)
'' alloca spazio per le connessioni
ReDim Glo.Connections(LBound(Glo.Pts()) To 
UBound(Glo.Pts())) As Type_VertexConnections
'' scansione per collegamenti
For J = LBound(Glo.Pts()) To UBound(Glo.Pts())
     ReDim Glo.Connections(J).JointedIdxes(LBound(Glo.Pts()) 
To UBound(Glo.Pts())) As Integer
     PtJ = Glo.Pts(J)
     Top = LBound(Glo.Pts()) - 1
     For Q = LBound(Glo.Pts()) To UBound(Glo.Pts())
         If Not (Q = J) Then
             With Glo.Pts(Q)
                 If ((((PtJ.X - .X) * (PtJ.X - .X)) + 
((PtJ.Y - .Y) * (PtJ.Y - .Y)) + ((PtJ.Z - .Z) * (PtJ.Z - 
.Z))) < DistanzaSoglia) Then
                     Top = Top + 1
                     Glo.Connections(J).JointedIdxes(Top) = Q
                 End If
             End With
         End If
     Next Q
     If (Top >= LBound(Glo.Pts())) Then
         Glo.Connections(J).HasSomeJoints = True
         ReDim Preserve 
Glo.Connections(J).JointedIdxes(LBound(Glo.Pts()) To Top) As 
Integer
     Else
         Glo.Connections(J).HasSomeJoints = False
         Erase Glo.Connections(J).JointedIdxes()
     End If
Next J
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub ShowPoints(ByRef Glo As Type_Glo, ByRef Pic_X As 
PictureBox, ByRef Pic_Y As PictureBox, ByRef Pic_Z As 
PictureBox, ByRef pic_3D As PictureBox, Optional ByVal 
UpDatePositions As Boolean = True)
Dim SogliaMotion As Double
Dim Radius As Double
Dim PointRagg As Double
Dim J As Integer
Dim NumDrawnEdges As Integer
Dim NumDrawnRadii As Integer
Dim ForeColorSpigoli As Integer
Dim ForeColorRaggi As Integer
Dim ShowMask As Long
Dim CurrClr As Long
Dim ColorFaintingStepSpigoli As Double
Dim ColorFaintingStepRaggi As Double
'' delega calcolo delle forze repulsive e lo spostamento dei 
vertici sulla sfera di confinamento
If (UpDatePositions = True) Then
     Call FzMoveAllVertices(Glo)
End If
'' delega calcolo delle connessioni attuali (a soglia)
Call FzDesumeConnections(Glo)
'' aggiorna il centro della viewport
Call FzCalculateBaricentro(Glo)
'' delega la ricostruzione completa della lista di oggetti 
grafici voluti (vertici-sfere, spigoli-edges, e raggi-diagonali)
If (mShowEdges.Checked = True) Then ShowMask = ShowMask Or 
EnumGraphicElemKind.Edge
If (mShowRadii.Checked = True) Then ShowMask = ShowMask Or 
EnumGraphicElemKind.Radius
If (mShowVertices.Checked = True) Then ShowMask = ShowMask 
Or EnumGraphicElemKind.VertexSphere
Call FzRebuildAllGraphicsObjectsList(Glo, ShowMask)
'' calcolo del raggio della sfera di confinamento
Radius = 0#
With Glo.Ranges
     If (Radius < Abs(.Max_X - .Min_X)) Then Radius = 
Abs(.Max_X - .Min_X)
     If (Radius < Abs(.Max_y - .Min_Y)) Then Radius = 
Abs(.Max_y - .Min_Y)
     If (Radius < Abs(.Max_Z - .Min_Z)) Then Radius = 
Abs(.Max_Z - .Min_Z)
End With
'' vitale : smorza progressivamente (progr. geometrica 
decrescente) l'entità degli spostamenti
'' laterali dei vertici. Ma non sotto una data soglia di un 
decimillesimo del raggio della sfera
Glo.MotionExtent = Glo.MotionExtent * 
ConstMotionExtentSlowingRate
SogliaMotion = Radius * ConstMinimalMotionRagPercent / 
Glo.NumeroVertici ' (GloNumeroVertici ^ 0.5)
If (Glo.MotionExtent < SogliaMotion) Then
     Glo.MotionExtent = SogliaMotion
End If
'' ritaglia del margine nelle graphics windows
Radius = Radius * ConstGraphZoomMargin
'' dimensiona le palline rappresentate per i vertici
PointRagg = Radius * ConstGraphCirclePercentRadius / 
Sqr(Glo.NumeroVertici)
'' Pic_X.Parent.Caption = Baricentro.X & ", " & Baricentro.Y 
& ", " & Baricentro.Z
Pic_X.Parent.Caption = "Raggio Sfera = " & Radius
'' PARTE GRAFICA effettiva
'' N.B. NON viene implementato un vero e proprio Z-Ordering 
dei punti
'' (ordinandoli dal più vicino al più lontano 
dall'oservatore, né una logica
'' completa di "hiding". Tuttavia la metà più vicina dei 
vertici è mostrata
'' in colori più chiari, e visualizzata in primo piano, i 
vertici della metà
'' distante sono più scuri e disegnati in background. Questo 
suggerisce
'' una minimale idea di profondità. In realtà, date le 
repulsioni, eccetto
'' che quando ci sono TANTI vertici, e solo per quelli 
all'equatore, questo
'' banale trucco funge egregiamente
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
If (ShowMask And EnumGraphicElemKind.Edge) Then
     If (Glo.NumeroSpigoli > 1) Then 
ColorFaintingStepSpigoli = 160# / CDbl(Glo.NumeroSpigoli) 
Else ColorFaintingStepSpigoli = 64#
     ForeColorSpigoli = 160
End If
If (ShowMask And EnumGraphicElemKind.Edge) Then
     ColorFaintingStepRaggi = 160# / CDbl(Glo.NumeroVertici)
     ForeColorRaggi = 160
End If
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
Call ReInitPicture(Pic_X, Radius)
'' delega azione di ZORDERING
Glo.CurrRotationAxis = x_Axis
Call FzSortArray_ZOrder(Glo)
Pic_X.FillColor = RGB(64, 128, 64)
NumDrawnEdges = 0
NumDrawnRadii = 0
For J = LBound(Glo.ZOrderIndexes()) To 
UBound(Glo.ZOrderIndexes())
     With Glo.ZOrderIndexes(J)
         Select Case .Kind
         Case EnumGraphicElemKind.Edge '' disegna le 
CONNESSIONI (edges) desunte
             If ((ShowMask And EnumGraphicElemKind.Edge) = 
0) Then GoTo Skip_Item_X
             CurrClr = ForeColorSpigoli - (NumDrawnEdges * 
ColorFaintingStepSpigoli)
             NumDrawnEdges = NumDrawnEdges + 1
             Pic_X.Line (Glo.Pts(.MainCoordIdx).Y, 
Glo.Pts(.MainCoordIdx).Z)-(Glo.Pts(.SecondaryCoordIdx).Y, 
Glo.Pts(.SecondaryCoordIdx).Z), RGB(CurrClr, CurrClr, CurrClr)
         Case EnumGraphicElemKind.Radius '' disegna i raggi 
(diagonali)
             If ((ShowMask And EnumGraphicElemKind.Radius) = 
0) Then GoTo Skip_Item_X
             CurrClr = ForeColorRaggi - (NumDrawnRadii * 
ColorFaintingStepRaggi)
             NumDrawnRadii = NumDrawnRadii + 1
             Pic_X.Line (Glo.Pts(.MainCoordIdx).Y, 
Glo.Pts(.MainCoordIdx).Z)-(0#, 0#), RGB(CurrClr, CurrClr, 
CurrClr)
         Case EnumGraphicElemKind.VertexSphere '' disegna le 
palle (VERTICI)
             If ((ShowMask And 
EnumGraphicElemKind.VertexSphere) = 0) Then GoTo Skip_Item_X
             Pic_X.Circle (Glo.Pts(.MainCoordIdx).Y, 
Glo.Pts(.MainCoordIdx).Z), PointRagg, RGB(32, 255, 32)
         End Select
     End With
Skip_Item_X:
Next J
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
Call ReInitPicture(Pic_Y, Radius)
'' delega azione di ZORDERING
Glo.CurrRotationAxis = y_Axis
Call FzSortArray_ZOrder(Glo)
Pic_Y.FillColor = RGB(128, 64, 64)
NumDrawnEdges = 0
NumDrawnRadii = 0
For J = LBound(Glo.ZOrderIndexes()) To 
UBound(Glo.ZOrderIndexes())
     With Glo.ZOrderIndexes(J)
         Select Case .Kind
         Case EnumGraphicElemKind.Edge '' disegna le 
CONNESSIONI (edges) desunte
             If ((ShowMask And EnumGraphicElemKind.Edge) = 
0) Then GoTo Skip_Item_Y
             CurrClr = ForeColorSpigoli - (NumDrawnEdges * 
ColorFaintingStepSpigoli)
             NumDrawnEdges = NumDrawnEdges + 1
             Pic_Y.Line (Glo.Pts(.MainCoordIdx).X, 
Glo.Pts(.MainCoordIdx).Z)-(Glo.Pts(.SecondaryCoordIdx).X, 
Glo.Pts(.SecondaryCoordIdx).Z), RGB(CurrClr, CurrClr, CurrClr)
         Case EnumGraphicElemKind.Radius '' disegna i raggi 
(diagonali)
             If ((ShowMask And EnumGraphicElemKind.Radius) = 
0) Then GoTo Skip_Item_Y
             CurrClr = ForeColorRaggi - (NumDrawnRadii * 
ColorFaintingStepRaggi)
             NumDrawnRadii = NumDrawnRadii + 1
             Pic_Y.Line (Glo.Pts(.MainCoordIdx).X, 
Glo.Pts(.MainCoordIdx).Z)-(0#, 0#), RGB(CurrClr, CurrClr, 
CurrClr)
         Case EnumGraphicElemKind.VertexSphere '' disegna le 
palle (VERTICI)
             If ((ShowMask And 
EnumGraphicElemKind.VertexSphere) = 0) Then GoTo Skip_Item_Y
             Pic_Y.Circle (Glo.Pts(.MainCoordIdx).X, 
Glo.Pts(.MainCoordIdx).Z), PointRagg, RGB(255, 32, 32)
         End Select
     End With
Skip_Item_Y:
Next J
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
Call ReInitPicture(Pic_Z, Radius)
'' delega azione di ZORDERING
Glo.CurrRotationAxis = z_Axis
Call FzSortArray_ZOrder(Glo)
Pic_Z.FillColor = RGB(64, 64, 128)
NumDrawnEdges = 0
NumDrawnRadii = 0
For J = LBound(Glo.ZOrderIndexes()) To 
UBound(Glo.ZOrderIndexes())
     With Glo.ZOrderIndexes(J)
         Select Case .Kind
         Case EnumGraphicElemKind.Edge '' disegna le 
CONNESSIONI (edges) desunte
             If ((ShowMask And EnumGraphicElemKind.Edge) = 
0) Then GoTo Skip_Item_Z
             CurrClr = ForeColorSpigoli - (NumDrawnEdges * 
ColorFaintingStepSpigoli)
             NumDrawnEdges = NumDrawnEdges + 1
             Pic_Z.Line (Glo.Pts(.MainCoordIdx).X, 
Glo.Pts(.MainCoordIdx).Y)-(Glo.Pts(.SecondaryCoordIdx).X, 
Glo.Pts(.SecondaryCoordIdx).Y), RGB(CurrClr, CurrClr, CurrClr)
         Case EnumGraphicElemKind.Radius '' disegna i raggi 
(diagonali)
             If ((ShowMask And EnumGraphicElemKind.Radius) = 
0) Then GoTo Skip_Item_Z
             CurrClr = ForeColorRaggi - (NumDrawnRadii * 
ColorFaintingStepRaggi)
             NumDrawnRadii = NumDrawnRadii + 1
             Pic_Z.Line (Glo.Pts(.MainCoordIdx).X, 
Glo.Pts(.MainCoordIdx).Y)-(0#, 0#), RGB(CurrClr, CurrClr, 
CurrClr)
         Case EnumGraphicElemKind.VertexSphere '' disegna le 
palle (VERTICI)
             If ((ShowMask And 
EnumGraphicElemKind.VertexSphere) = 0) Then GoTo Skip_Item_Z
             Pic_Z.Circle (Glo.Pts(.MainCoordIdx).X, 
Glo.Pts(.MainCoordIdx).Y), PointRagg, RGB(32, 32, 255)
         End Select
     End With
Skip_Item_Z:
Next J
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
'' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
- - - - - - - - - - - - - - -
'' VISTA TRIDIMENSIONALE
Call ReInitPicture(pic_3D, Radius)
Dim X1 As Double, Y1 As Double, Z1 As Double, X2 As Double, 
Y2 As Double, Z2 As Double
pic_3D.FillColor = RGB(92, 92, 32)
NumDrawnEdges = 0
NumDrawnRadii = 0
For J = LBound(Glo.ZOrderIndexes()) To 
UBound(Glo.ZOrderIndexes())
     With Glo.ZOrderIndexes(J)
         Select Case .Kind
         Case EnumGraphicElemKind.Edge '' disegna le 
CONNESSIONI (edges) desunte
             If ((ShowMask And EnumGraphicElemKind.Edge) = 
0) Then GoTo Skip_Item_3D
             Z1 = ConstUnoSuRadiceDue * Glo.Pts(.MainCoordIdx).Z
             X1 = Glo.Pts(.MainCoordIdx).X + Z1
             Y1 = Glo.Pts(.MainCoordIdx).Y + Z1
             Z2 = ConstUnoSuRadiceDue * 
Glo.Pts(.SecondaryCoordIdx).Z
             X2 = Glo.Pts(.SecondaryCoordIdx).X + Z2
             Y2 = Glo.Pts(.SecondaryCoordIdx).Y + Z2
             CurrClr = ForeColorSpigoli - (NumDrawnEdges * 
ColorFaintingStepSpigoli)
             NumDrawnEdges = NumDrawnEdges + 1
             pic_3D.Line (X1, Y1)-(X2, Y2), RGB(CurrClr, 
CurrClr, CurrClr)
         Case EnumGraphicElemKind.Radius '' disegna i raggi 
(diagonali)
             If ((ShowMask And EnumGraphicElemKind.Radius) = 
0) Then GoTo Skip_Item_3D
             Z1 = ConstUnoSuRadiceDue * Glo.Pts(.MainCoordIdx).Z
             X1 = Glo.Pts(.MainCoordIdx).X + Z1
             Y1 = Glo.Pts(.MainCoordIdx).Y + Z1
             CurrClr = ForeColorRaggi - (NumDrawnRadii * 
ColorFaintingStepRaggi)
             NumDrawnRadii = NumDrawnRadii + 1
             pic_3D.Line (X1, Y1)-(0#, 0#), RGB(CurrClr, 
CurrClr, CurrClr)
         Case EnumGraphicElemKind.VertexSphere '' disegna le 
palle (VERTICI)
             If ((ShowMask And 
EnumGraphicElemKind.VertexSphere) = 0) Then GoTo Skip_Item_3D
             Z1 = ConstUnoSuRadiceDue * Glo.Pts(.MainCoordIdx).Z
             X1 = Glo.Pts(.MainCoordIdx).X + Z1
             Y1 = Glo.Pts(.MainCoordIdx).Y + Z1
             pic_3D.Circle (X1, Y1), PointRagg, RGB(128, 128, 0)
         End Select
     End With
Skip_Item_3D:
Next J
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub ReInitPicture(ByRef Pic As PictureBox, ByVal 
Radius As Double)
Pic.AutoRedraw = False
Pic.AutoSize = False
Pic.ClipControls = False
Pic.BackColor = RGB(255, 255, 255)
Pic.Scale (-Radius, -Radius)-(Radius, Radius)
Pic.FillStyle = 0 'solid
Pic.DrawWidth = 3
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub cmd_Go_Stop_Click()
Static LastRunMode As Boolean
If (LastRunMode = True) Then
     LastRunMode = False '' interrompe ottimizzazione
     cmd_Go_Stop.Caption = "GO"
Else
     cmd_Go_Stop.Caption = "STOP"
     LastRunMode = True '' avvia ottimizzazione
     Do
         DoEvents '' NON TOGLIERE -> blocca la GUI
         If (LastRunMode = False) Then '' rileva la 
condizione di uscita (tramite questa stessa routine, che 
cicla modalità ad ogni chiamata dispari)
             Exit Do
         End If
         '' delega l'aggiornamento di posizioni e 
visualizzazione
         Call ShowPoints(Glo, pict_View_X, pict_View_Y, 
pict_View_Z, pict_View_3D, True)
         DoEvents '' NON TOGLIERE -> blocca la GUI
     Loop
End If
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub Form_Load()
Glo.NumeroVertici = ConstDefaultNumVertici
Glo.RotazAngle = ConstDefaultRotazAngle
Glo.CurrRotationAxis = z_Axis
Glo.PercentDistanceSogliaJointed = Sqr(2#) * 0.999
Call InitPoints(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, True)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub Form_Resize()
Me.Scale (-1, -1)-(1004, 1104)
pict_View_Z.Move 1, 1, 500, 500
pict_View_X.Move 1, 502, 500, 500
pict_View_Y.Move 502, 1, 500, 500
pict_View_3D.Move 502, 502, 500, 500
cmd_Go_Stop.Move 1, 1001, 500, 100
pict_View_Z.Scale (-1.05, -1.05)-(1.05, 1.05)
pict_View_X.Scale (-1.05, -1.05)-(1.05, 1.05)
pict_View_Y.Scale (-1.05, -1.05)-(1.05, 1.05)
pict_View_3D.Scale (-1.05, -1.05)-(1.05, 1.05)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub Form_Unload(Cancel As Integer)
End
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub mExportVerticesCoordinates_Click()
Dim J As Integer
Dim WrPos As Integer
Dim Txt As String
Dim Field As String
Dim NotPad As Double
Txt = Space$(Glo.NumeroVertici * 64)
WrPos = 1
For J = LBound(Glo.Pts()) To UBound(Glo.Pts())
     With Glo.Pts(J)
         Field = "Vertex " & Format$(J, "000") & " = {(" & 
Format$(.X, "###0.000#######") & ") (" & Format$(.Y, 
"###0.000#######") & ") (" & Format$(.Z, "###0.000#######") 
& ")}" & vbCrLf
         Mid$(Txt, WrPos) = Field
         WrPos = WrPos + Len(Field)
     End With
Next J
Txt = Left$(Txt, WrPos + 1)
Clipboard.Clear
Clipboard.SetText Txt, vbCFText
MsgBox "Vertices coordinates copied to clipboard", , 
"Exporting Done"
NotPad = Shell("notepad", vbNormalFocus)
Call AppActivate(NotPad, True)
SendKeys "^v", True
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub mExportVerticesCoordinationNumbers_Click()
Dim J As Integer
Dim Q As Integer
Dim WrPos As Integer
Dim Txt As String
Dim Field As String
Dim NotPad As Double
Txt = Space$(Glo.NumeroVertici * Glo.NumeroVertici * 15)
WrPos = 1
For J = LBound(Glo.Connections()) To UBound(Glo.Connections())
     Field = "Vertex : " & J & vbCrLf
     Mid$(Txt, WrPos) = Field
     WrPos = WrPos + Len(Field)
     With Glo.Connections(J)
         If (.HasSomeJoints = True) Then
             Field = "Connected to " & 
(UBound(.JointedIdxes()) - LBound(.JointedIdxes()) + 1) & " : ("
             Mid$(Txt, WrPos) = Field
             WrPos = WrPos + Len(Field)
             Field = ""
             For Q = LBound(.JointedIdxes()) To 
UBound(.JointedIdxes())
                 Field = Field & .JointedIdxes(Q) & ", "
             Next Q
             Mid$(Txt, WrPos) = Field
             WrPos = WrPos + Len(Field) - 2
             Field = ")" & vbCrLf
             Mid$(Txt, WrPos) = Field
             WrPos = WrPos + Len(Field)
         Else
             Field = "None"
             Mid$(Txt, WrPos) = Field
             WrPos = WrPos + Len(Field)
         End If
     End With
Next J
Txt = Left$(Txt, WrPos + 1)
Clipboard.Clear
Clipboard.SetText Txt, vbCFText
MsgBox "Vertices connections copied to clipboard", , 
"Exporting Done"
NotPad = Shell("notepad", vbNormalFocus)
Call AppActivate(NotPad, True)
SendKeys "^v", True
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub mJointVertexThreshold_Click()
Dim Soglia As Double
Soglia = InputValue("Enter the relative distance threshold 
(the reference being the smallest distance found) below 
which two vertices are considered joined. The lower limit is 
strictly 1.0 (such that only one connection is found, the 
shortest one !), the upper limit cannot exceed square root 
of 2 (so that a square facet is preserved as such).", 
"Connected Vertices Threshold", 
Glo.PercentDistanceSogliaJointed)
If (Soglia <= 1.01) Or (Soglia >= Sqr(2#)) Then
     MsgBox "Illegal threshold value entered (out of 
range)", , "Bad Input"
     Beep
     Exit Sub
End If
Glo.PercentDistanceSogliaJointed = Soglia
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub mNumeroVertici_Click()
Dim Vertex As Double
Vertex = InputValue("Enter the desired vertices number, 
between 4 and " & ConstMaxNumVertici & ". (Note that a high 
N° may causes slowing of the calculations, the algorithm 
being of O(2) order vs. N°).", "Number of Vertices", 
ConstDefaultNumVertici)
If (CInt(Vertex) < 4) Or (CInt(Vertex) > ConstMaxNumVertici) 
Then
     MsgBox "Illegal vertices value entered (out of range)", 
, "Bad Input"
     Beep
     Exit Sub
End If
Glo.NumeroVertici = CInt(Vertex)
Call InitPoints(Glo)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub mShowEdges_Click()
mShowEdges.Checked = Not mShowEdges.Checked
End Sub
Private Sub mShowRadii_Click()
mShowRadii.Checked = Not mShowRadii.Checked
End Sub
Private Sub mShowVertices_Click()
mShowVertices.Checked = Not mShowVertices.Checked
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub pict_View_X_MouseMove(Button As Integer, Shift 
As Integer, X As Single, Y As Single)
Static OldX As Single, OldY As Single
If (Button = 0) And (Shift = 0) Then Exit Sub
Dim DeltaX As Double
Dim DeltaY As Double
DeltaX = X - OldX
DeltaY = Y - OldY
OldX = X
OldY = Y
Glo.CurrRotationAxis = z_Axis
Glo.RotazAngle = ConstDuePIGreco * (DeltaX / 
pict_View_X.ScaleWidth)
Call FzRotateAllVertices(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
Glo.CurrRotationAxis = y_Axis
Glo.RotazAngle = ConstDuePIGreco * (DeltaY / 
pict_View_X.ScaleHeight)
Call FzRotateAllVertices(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub pict_View_y_MouseMove(Button As Integer, Shift 
As Integer, X As Single, Y As Single)
Static OldX As Single, OldY As Single
If (Button = 0) And (Shift = 0) Then Exit Sub
Dim DeltaX As Double
Dim DeltaY As Double
DeltaX = OldX - X
DeltaY = Y - OldY
OldX = X
OldY = Y
Glo.CurrRotationAxis = x_Axis
Glo.RotazAngle = ConstDuePIGreco * (DeltaY / 
pict_View_X.ScaleWidth)
Call FzRotateAllVertices(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
Glo.CurrRotationAxis = z_Axis
Glo.RotazAngle = ConstDuePIGreco * (DeltaX / 
pict_View_X.ScaleHeight)
Call FzRotateAllVertices(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------
Private Sub pict_View_Z_MouseMove(Button As Integer, Shift 
As Integer, X As Single, Y As Single)
Static OldX As Single, OldY As Single
If (Button = 0) And (Shift = 0) Then Exit Sub
Dim DeltaX As Double
Dim DeltaY As Double
DeltaX = OldX - X
DeltaY = OldY - Y
OldX = X
OldY = Y
Glo.CurrRotationAxis = x_Axis
Glo.RotazAngle = ConstDuePIGreco * (DeltaY / 
pict_View_X.ScaleWidth)
Call FzRotateAllVertices(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
Glo.CurrRotationAxis = y_Axis
Glo.RotazAngle = ConstDuePIGreco * (DeltaX / 
pict_View_X.ScaleHeight)
Call FzRotateAllVertices(Glo)
Call ShowPoints(Glo, pict_View_X, pict_View_Y, pict_View_Z, 
pict_View_3D, False)
End Sub
'' 
----------------------------------------------------------------------------------------------------------------------------------------------------