CATEGORII DOCUMENTE |
Introducere in dezvoltarea de aplicatii VisualBasic in mediul AutoCAD
Cuprins:
1. De ce Visual Basic ?
2. Cunostinte anterioare necesare
3. Resurse soft necesare
4. Conventii
Setarea referintelor in VB la aplicatia Acad
Deschiderea aplicatiei AutoCAD din Visual Basic
Modelul de obiecte AutoCAD
Metodele , proprietatile si evenimentele obiectelor (de ex. pt. AcadCircle):
Prezentarea pasilor pentru crearea unei aplicatii simple care genereaza un obiect in ACAD si apoi realizeaza deplasarea lui pe o traiectorie circulara
Descrierea cerintelor aplicatiei
Descrierea modului de dezvoltare a aplicatiei (aplicatia exista in directorul Aplicatie VB-ACAD)
10. Lucrul cu fisiere de tip *.ini
11. Tipuri de aplicatii ce pot fi dezvoltate utilizand combinarea limbajului Visual Basic cu mediul de proiectare asistata AutoCAD
12. Comentarii
De ce Visual Basic ?
S-a ales limbajul Visual Basic (VB) pentru dezvoltarea aplicatiilor in mediul AutoCAD din mai urmatoarele considerente:
se pot realiza relativ rapid aplicatii simple de generare a unor modele geometrice sau simulari in 3D care nu sunt critice din punct de vedere a vitezei de executie;
permite realizarea unor interfete "prietenoase" de tip Windows;
Este studiat de studentii de la departamentul Mecatronica;
Intelegerea modului de lucru cu ierarhia de obiecte din mediul de proiectare asistata AutoCAD, permite deprinderea cu usurinta prin similitudine a dezvoltarii unor aplicatii in alte medii care permit dezvoltarea in Visual Basic sau Visual Basic pt. Aplicatii : Catia, Word, Excel, PowerPoint, etc;
Limbajul Visual Basic permite integrarea a numeroase aplicatii.
Documentatia Help a aplicatiei AutoCAD pentru dezvoltarea cu VB este foarte detaliata si contine numeroase exemple.
Cunostinte anterioare necesare
Sunt necesare cunostinte de nivel mediu de operare in AutoCAD si cunostinte minimale de programare in Visual Basic.
Resurse soft necesare
Pentru dezvoltarea aplicatiei prezentate s-a utilizat VisualBasic 6 si AutoCAD versiunea 2000.
Conventii
Pentru usurinta lucrului in echipa la aplicatii VB se propune respectarea unor conventii:
Denumiri de functii:
Numele functiilor sau subrutinelor trebuie sa fie sugestive (adica din numele functiei trebuie sa ne dam seama ce face functia respectiva). Ex. : DeseneazaCerc, DeplaseazaPaleta, etc.
Headere de functii:
'Nume functie: NumeFunc
'Scop: se descrie ce face functia
'Parametri de intrare:
paramA descriere param A (ce reprezinta)
paramB descriere param B
'Valoare returnata :
'Autor:
'Data:
In cazul subrutinelor nu se trece valoarea returnata.
Denumiri de controale VB:
Pentru usurinta de regasire a controalelor VB (casete de text, casete de imagini, .) se utilizeaza o notatie cu prefixe.
Caseta de text txt. ex : txtDiamCerc
Combo box cbo.
Caseta pt. imagine pic.
Frame fra.
Forme frm.
Denumiri de variabile:
Pentru variabile simple (integer , double , string .) se utilizeaza prefixe. Ex:
Integer int.
Double dbl.
String str.
Colectie col.
Long l.
Setarea referintelor in VB la aplicatia Acad
Specificarea referintelor este necesara pentru a permite accesarea
obiectelor ActiveX puse la dispozitie de mediul AutoCAD.
Pentru specificarea referintelor la aplicatia AutoCAD se marcheaza referintele prezentate in captura urmatoare apoi se confirma cu Ok.
Deschiderea aplicatiei AutoCAD din Visual Basic
Pentru deschiderea aplicatiei AutoCAD din VB se utilizeaza functia OpenACAD :
Public Sub OpenAcad()
'Connect to the AutoCAD application
On Error Resume Next
Set g_acadApp = GetObject (, 'AutoCAD.Application')
If Err Then
Set g_acadApp = CreateObject ('AutoCAD.Application')
If Err Then
MsgBox Err.Description
End If
End If
g_acadApp.Visible = True
Modelul de obiecte AutoCAD
Pentru a ajunge la Help VB din AutoCAD se deschide Help AutoCAD (de ex. Tasta F1 cu Aplicatia AutoCAD activa)
Pentru generarea unui obiect de tip cerc , daca aplicatia a fost deschisa cu functia OpenAcad fiind definita ca variabila globala in modul cu Public g_acadApp As AcadApplication atunci pentru adaugarea unui cerc in modul :
Dim CerculMeu As AcadCircle
Set CerculMeu = g_acadApp.ActiveDocument.ModelSpace.AddCircle(centru, raza) unde variabilele centru si raza au fost definite anterior.
Metodele , proprietatile si evenimentele obiectelor (de ex. pt. AcadCircle):
La accesarea help-ului pentru o operatie concreta se utilizeaza tasta F1 dupa selectarea obiectului. De exemplu, vreau sa desenez un cerc si nu stiu ce metoda trebuie sa folosesc si a carui obiect. Declar obiectul cerc cu :
apoi avand selectat ca in fig. de mai sus actionez tasta F1 si apare fereastra:
iar la Ok se deschide help relativ la obiectul cerc
unde se observa ca e necesara apelarea metodei AddCircle. Daca dau click pe AddCircle se deschide fereastra cu help pt. metoda respectiva
iar pentru majoritatea metodelor exista exemple asa ca prin click pe Example se deschide o fereastra cu exemplu de adaugare a unui cerc in spatiul Model.
Din exemple se poate copia in codul sursa (Selectare->Copy->Paste)
9. Prezentarea pasilor pentru crearea unei aplicatii simple care genereaza un obiect in ACAD si apoi realizeaza deplasarea lui pe o traiectorie circulara
Descrierea aplicatiei:
Proiectul va avea o forma in care se specifica date pentru obiectul care se genereaza si pentru deplasarea acestuia. Se vor crea 2 cilindrii concentrici cu inaltime egala cu centrul cercului de baza in origine ca model solid, apoi unul va fi extras din celalalt. Dupa generarea obiectului acesta va fi rotit pe o traiectorie circulara, incrementul de deplasare fiind specificat de utilizator intr-o caseta de dialog (forma VB).
Descrierea modului de dezvoltare a aplicatiei (aplicatia exista in directorul Aplicatie VB-ACAD)
Creez o noua aplicatie Visual Basic (de tip "Standard EXE", apoi setez referintele la aplicatia AutoCAD (vezi. Pt. 5). Aplicatia are implicit o forma pe care o voi folosi la specificarea datelor necesare aplicatiei.
Pentru ca sa pot declara variabile globale in toata aplicatia (ma gandesc ca e posibil ca aplicatia sa se dezvolte si sa am nevoie mai multe forme) creez un modul care va fi modulul principal al aplicatiei si il denumesc de exemplu modMain.
Pentru ca sa fiu avertizat in cazul in care uit sa declar o variabila (declararea variabilelor e utila pentru ca sa nu ajung la erori datorate faptului ca trimit ca parametru un tip de variabila si functia asteapta alt tip de parametru) introduc la inceputul formelor si modulelor "Option Explicit".
Pentru ca sa am acces la obiectele AutoCAD, in modul declar aplicatia AutoCAD ca variabila globala:
apoi creez functia "OpenAcad" prezentata la pct. 6.
Din cerintele aplicatiei se observa ca este necesar ca utilizatorul sa precizeze date pentru diametrul cilindrului mare si a celui mic, inaltimea cilindrilor, raza cercului care reprezinta traiectoria si numarul de pasi (de cate ori se va deplasa obiectul pana la parcurgerea circumferintei cercului pentru a da impresia unei deplasari continue). Deci am nevoie de 5 textBox-uri unde utilizatorul va da valorile si 5 etichete pentru specificarea semnificatiei parametrilor. Adaug controalele necesare pe forma:
Denumesc forma si proiectul apoi salvez aplicatia (poate se ia curentul J
Schimb textul formei si al etichetelor. De ex. pt. eticheta:
Dau nume sugestive fiecarui control de pe forma (cu prefixe lbl pentru eticheta, txt pentru caseta de text).
Pe butonul de Cancel doar descarc forma "Unload Me". "Me" refera obiectul curent.
Private Sub cmdCancel_Click()
Unload Me
Pe evenimentul de click pe buton de comanda Ok verific daca valorile scrise in casetele de text sunt valide si au fost completate toate casetele de text.
Private Sub cmdOk_Click()
Dim colDate As New Collection 'colectie pentru valorile introduse de utilizator
Dim isValid As Boolean 'True - daca datele introduse de utilizator sunt valide
Dim strHandleSolid As String 'handle-ul obiectului solid creat
isValid = VerificaDateUser(colDate)
If isValid Then 'daca datele introduse de utilizator sunt valide
strHandleSolid = GenereazaObiectSolid(colDate)
End If
'Golesc colectia cu datele introduse de utilizator
Dim i As Integer
While i < colDate.Count
colDate.Remove (1)
Wend
Set colDate = Nothing
Unload Me
End Sub
Handle-ul obiectului reprezinta un sir de caractere cu care obiectul este identificat in AutoCAD. Handle-ul obiectului nu se pierde la inchiderea sesiunii de lucru cu aplicatia AutoCAD . Acest lucru este foarte util deoarece ne permite regasirea unui obiect in desen la o noua incarcare a desenului care contine obiectul. Pentru acesta este necesar sa salvam Handle-ul obiectului (de exemplu intr-un fisier text). De exemplu, daca realizam un model geometric al unui robot si intr-o baza de date sau intr-un fisier indicam ca axei 2 modelate prin linie al robotului ii corespunde Handle-ul "2B" atunci la o noua incarcare a desenului care contine modelul robotului axa a doua poate fi gasita prin metoda HandleToObject("2B").
'Nume functie: VerificaDateUser
'Scop: Verfica daca datele inscrise de utilizator sunt valide
'Parametri de intrare:
colDate Colectie cu datele specificate de utilizator in forma (se completeaza in functie)
'Valoare returnata :
Valoarea booleana True daca valorile sunt corecte si False in caz contrar
'Autor: Maci Claudiu
'Data: 2002.03
Private Function VerificaDateUser(ByRef colDate As Collection) As Boolean
'Initializez valoarea de iesire
'(valoarea este returnata prin numele functiei)
VerificaDateUser = False
'-------- ----- ------ ----- ----- -----------
'Verific daca utilizatorul a specificat toate valorile
'-------- ----- ------ ----- ----- -----------
If txtDiamMare.Text = '' Then 'Daca nu a introdus nici valoare in caseta de text
MsgBox 'Specificati o valoare pentru diametrul cilindrului mare!'
txtDiamMare.SetFocus 'activeaza caseta de text
Exit Function
End If
..
'-------- ----- ------ ----- ----- -----------
'Verific daca utilizatorul a specificat o valoare numerica
'si nu a introdus din greseala siruri de caractere
'-------- ----- ------ ----- ----- -----------
If Not IsNumeric(txtDiamMare.Text) Then
MsgBox 'Valoarea trebuie sa fie numerica !'
txtDiamMare.SetFocus
Exit Function
'-------- ----- ------ ----- ----- -----------
'Setarea unor variabile cu valorile introduse de utilizator
'-------- ----- ------ ----- ----- -----------
Dim dblDiamMare As Double
Dim dblDiamMic As Double
Dim dblInaltime As Double
Dim dblRazaCercTraiectorie As Double
Dim intNrPasi As Integer
dblDiamMare = CDbl(txtDiamMare.Text)
dblDiamMic = CDbl(txtDiamMic.Text)
dblInaltime = CDbl(txtInaltime.Text)
dblRazaCercTraiectorie = CDbl(txtRazaTraiectorie.Text)
intNrPasi = CInt(txtNrPasiDeplasare.Text)
'-------- ----- ------ ----- ----- -----------
'Verific daca datele sunt valide din punctul de vedere al constringerilor
'-------- ----- ------ ----- ----- -----------
'Verific daca toate valorile sunt pozitive (si implicit diferite de 0)
If (dblDiamMare <= 0) Or (dblDiamMic <= 0) Or (dblInaltime <= 0) Or _
(dblRazaCercTraiectorie <= 0) Or (intNrPasi <= 0) Then
MsgBox 'Toate valorile trebuie sa fie pozitive '
Exit Function
End If
If (dblDiamMic >= dblDiamMare) Then
MsgBox 'Valoarea diametrului pt cilindrul mare ' & _
'trebuie sa fie mai mare decit cea pt cilindrul mic!'
Exit Function
End If
'-------- ----- ------ ----- ----- -----------
'Formez colectia cu valorile introduse de utilizator
'-------- ----- ------ ----- ----- -----------
colDate.Add dblDiamMare, 'DiamCilMare'
colDate.Add dblDiamMic, 'DiamCilMic'
colDate.Add dblInaltime, 'InaltimeCilindrii'
colDate.Add dblRazaCercTraiectorie, 'RazaCercTraiectorie'
colDate.Add intNrPasi, 'NrPasiDeplasare'
VerificaDateUser = True
End Function 'VerificaDateUser
In cazul in care datele introduse de utilizator sunt corecte se apeleaza functia pentru generarea corpului solid. Cand se creeaza o functie, este o buna deprindere sa se scrie intai comentariile in functie si apoi sa se completeze codul.
'Nume functie: GenereazaObiectSolid
'Scop: Verifica daca datele inscrise de utilizator sunt valide
'Parametri de intrare:
dblDiamMare Diametrul cilindrului mare
dblDiamMic Diametrul cilindrului mic
dblInaltime Inaltimea cilindrilor
'Valoare returnata :
Handle-ul obiectului solid rezultat in urma extragerii cilindrului
mic din cel mare
'Autor: Maci Claudiu
'Data: 2002.03
Private Function GenereazaObiectSolid(dblDiamMare As Double, dblDiamMic As Double, _
dblInaltime As Double) As String
'Se dechide aplicatia AutoCAD
'Se creeaza un nou document care va fi ducumentul activ
'Se genereaza cei doi cindrii
'Se extrage cilindru mic din cel mare
'Se returneaza handle-ul obiectului rezultat
End Function
Pentru deschiderea aplicatiei AutoCAD se apeleaza functia OpenAcad prezentata anterior.
Se creeaza un document nou. Pentru crearea unui
document nou ma uit in ierarhia de obiecte AutoCAD in help.
Urmand procedura de la punctul 8 observ ca trebuie sa adaug un nou document la colectia de documente a aplicatiei AutoCAD a carei referinta este data de variabila g_acadApp declarata global in modulul modMain al aplicatiei.:
g_acadApp.Documents.Add
Trebuie sa creez cilindrii cu model solid deci cilindri vor fi obiecte AutoCAD de tip 3DSolid:
generarea celor 2 cilindri si formarea corpului rezultat prin extragerea lor
deplsarea cilindrilor pe un cerc (calcularea punctelor cu polar)
Dupa crearea celor 2 cilindrii extrag pe cel mic din cel mare printr-o operatie booleana aplicata cilindrului mare:
'Se extrage cilindrul mic din cel mare
'Se extrage cilindru mic din cel mare
cil1.Boolean acSubtraction, cil2
Pentru schimbarea culorii obiectului se da o alta valoare proprietatii color a obiectului:
'Se da culoarea rosie obiectului selectat
cil1.Color = acRed
Pentru vizualizarea obiectului creat se aplica un zoom si se schimba unghiul de vedere:
'Se schimba unghiul de vedere pt. perspectiva
Dim dblDir(0 To 2) As Double
dblDir(0) = 1#
dblDir(1) = 1#
dblDir(2) = 1#
g_acadApp.ActiveDocument.ActiveViewport.Direction = dblDir
g_acadApp.ActiveDocument.ActiveViewport = g_acadApp.ActiveDocument.ActiveViewport
'Se realizeaza un zoom pt vizualizarea obiectului creat
Desenez cercul care reprezinta traiectoria:
Dim TraiectoorieCerc As AcadCircle
Dim dblCentruCerc(0 To 2) As Double
dblCentruCerc(0) = 0
dblCentruCerc(1) = 0
dblCentruCerc(2) = 0
Set TraiectoorieCerc = g_acadApp.ActiveDocument.ModelSpace.AddCircle(dblCentruCerc, dblRazaCercTraiectorie)
ZoomExtents
Creez o functie pentru calculul punctelor in care se va deplasa pe traiectorie obiectul solid. Pentru a da impresia unei deplasari continue, obiectul se va deplasa succesiv pe distante mici. In aplicatii de simulare a deplasarii obiectelor, numarul de incremente cu care este divizata traiectoria depinde de viteza de procesare a sistemului de calcul (si de placa grafica in special) , de complexitatea obiectului care se deplaseaza si de modul de vizualizare (cadru de sarma sau solid). Daca se alege un numar prea mic de incremente de divizare a traiectoriei, miscarea pare sacadata. Alegerea unui numar prea mare de incremente face ca miscarea sa se desfasoare foarte incet, ceea ce poate duce la pierderea realismului miscarii simulate.
'Nume functie: CalculeazaPuncte
'Scop: Calculeaza punctele succesive pt deplasarea pe traiectorie
'Parametri de intrare:
' dblRaza Raza cercului care reprezinta traiectoria
' intNrPasi Numarul de puncte succesive cu care este aproximata traiectoria
'Valoare returnata :
Colectie cu punctele succeive pe traiectorie
'Autor: Maci Claudiu
'Data: 2002.03
Private Function CalculeazaPuncte(dblRaza As Double, intNrPasi As Integer) As Collection
Dim ColPuncte As New Collection
Dim dblPtCurent(0 To 2) As Double
Dim dblOrig(0 To 2) As Double
dblOrig(0) = 0
dblOrig(1) = 0
dblOrig(2) = 0
'Calculez valoarea incrementului unghiular
Dim dblUnghiElem As Double
dblUnghiElem = 2 * 3.141 / intNrPasi
'Adaug in colectie punctul in care se afla initial obiectul (0,0,0)
ColPuncte.Add dblOrig
Dim i As Integer
For i = 0 To intNrPasi
'Se calculeaza coordonatele fiecarui punct
dblPtCurent = g_acadApp.ActiveDocument.Utility.PolarPoint(dblOrig, i * dblUnghiElem, dblRaza)
ColPuncte.Add dblPtCurent
Next i
Set CalculeazaPuncte = ColPuncte
End Function
Creez o functie pentru simularea in 3D a deplasarii obiectelor. Aceasta va prelua ca parametrii Handle-ul obiectului care trebuie deplasat si colectia de puncte pe traiectorie calculata de functia anterioara:
'Nume functie: DeplaseazaObiect
'Scop: 'Realizeaza deplasarea unui obiect pe o traiectorie specificata
prin puncte
'Parametri de intrare:
' strHandleSolid Handle-ul obiectului care se deplaseaza
' colPtTraiectorie colectie cu punctele de pe traiectorie
'Valoare returnata :
Colectie cu punctele succeive pe traiectorie
'Autor: Maci Claudiu
'Data: 2002.03
Private Function DeplaseazaObiect(strHandleSolid As String, _
colPtTraiectorie As Collection) As Boolean
Dim IsDeplasareOk As Boolean
IsDeplasareOk = False
'Se obtine referinta la obiect pornind de la handle
Dim objSolid As AcadEntity
Set objSolid = g_acadApp.ActiveDocument.HandleToObject(strHandleSolid)
'Se deplaseaza succesiv obiectul in punctele din colectia data ca parametru
Dim i As Integer
For i = 1 To (colPtTraiectorie.Count - 1)
objSolid.Move colPtTraiectorie(i), colPtTraiectorie(i + 1)
objSolid.Update
Next i
IsDeplasareOk = True
DeplaseazaObiect = IsDeplasareOk
End Function ' DeplaseazaObiect
S-a mai introdus in functia de generare a cilindrilor o linie de cod pentru a seta modul de vizualizare sa fie de tip umbrit :
g_acadApp.ActiveDocument.SendCommand 'shademode' & vbCr & 'flat' & vbCr
Metoda SendCommand este modalitatea de transmitere pentru executie a unei comenzi AutoCAD. Sirul "vbCr" reprezinta echivalentul unui "Enter".
Lucrul cu fisiere de tip *.ini
Pentru stocarea unor date necesare aplicatiilor exista posibilitatea de salvare a datelor in fisiere de tip *.ini . In continuare se prezinta pasii necesari pentru salvarea valorilor introduse de utilizator in casetele de text astfel ca dupa ce s-a rulat cu succes aplicatia , la apelul urmator al aplicatiei sa vor propune ca valori implicite ultimele valori indicate de utilizator.
Pentru a permite apelarea functiilor API (puse la dispozitie de mediul Windows) pentru salvarea si regasirea datelor in/din fisiere de tip ini trebuie sa se introduca in modulul aplicatiei :
'Declaratii pentru citire din fisier si scriere in fisier de tip ini:
Private Declare Function GetPrivateProfileString Lib 'kernel32' Alias 'GetPrivateProfileStringA' (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
si
Private Declare Function WritePrivateProfileString Lib 'kernel32' Alias 'WritePrivateProfileStringA' (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
Fisierul de tip ini al aplicatiei are o sectiuni (ex. DateCilindri), parametrii (ex. DiametruCilMare) si valori pentru parametrii (ex. 45).
[DateCilindri]
DiametruCilMare=45
DiametruCilMic=34
InaltimeCilindrii=4
[Traiectorie]
RazaCercTraiectorie=100
NrIncremDivizareTraiectorie=30
Pentru lucrul cu fisierul ini s-au creat functiile "SetParamValue" si "GetParamValue" . Functiile au fost create in modul pentru a putea fi utilizate in toate formele sau clasele aplicatiei. Prin App.Path & 'date.ini' s-a indicat ca fisierul ini sa fie creat si accesat in calea compusa din calea aplicatiei Visual Basic (app) si numele fisierului ("date.ini"). Fisierul se creeaza automat la primul apel de scriere in fisier.
'Seteaza valoarea unui parametru in fisierul ini al aplicatiei
Public Sub SetParamValue(strSection As String, strParam As String, strValue As String)
Dim Ret As String, NC As Long
'scrie setarile in fisierul ini al aplicatiei
WritePrivateProfileString strSection, strParam, strValue, App.Path & 'date.ini'
End Sub
'Extrage valoarea unui parametru din fisirul ini al aplicatiei
Public Function GetParamValue(strSection As String, strParam As String) As String
Dim Ret As String
Dim NC As Long
'Creeaza un buffer
Ret = String(255, 0)
'Retrieve the string
NC = GetPrivateProfileString(strSection, strParam, 'default', Ret, 255, App.Path & 'date.ini')
'NC is the number of characters copied to the buffer
If NC <> 0 Then Ret = Left$(Ret, NC)
GetParamValue = Ret
End Function
Scrierea valorilor se face inainte de inchiderea formei cu valorile indicate de utilizator (in cazul in car acestea sunt valide) prin apeluri de forma:
SetParamValue 'DateCilindrii', 'DiametruCilMare', CStr(dblDiamMare)
Citirea valorilor se va realiza la incarcarea formei (pe Load-ul formei) prin apeluri de forma:
txtDiamMare.Text = GetParamValue('DateCilindrii', 'DiametruCilMare')
Tipuri de aplicatii ce pot fi dezvoltate utilizand combinarea limbajului Visual Basic cu mediul de proiectare asistata AutoCAD:
aplicatii de realizare automata a desenelor de executie si a modelelor geometrice 3D a unor repere pe baza datelor specificate de utilizator in casete de dialog sau prin utilizarea datelor preluate din fisiere ( unde au fost salvate anterior de alte aplicatii);
simularea functionarii unor mecanisme;
aplicatii specifice domeniului realitatii virtuale;
pogramarea off-line in medii grafice a robotilor (ex. aplicatia "RobSim" );
Comentarii
Pentru realizarea unor aplicatii profesionale, atunci cand este cazul, datele aplicatiei pot fi stocate in baze de date Acces . Daca exista operatii critice din punctul de vedere al vitezei de executie, in cazul unor aplicatii care lucreaza in mediul AutoCAD este de preferat utilizarea programarii in ObjectARX (C++) care opereaza in acelasi spatiu de memorie cu AutoCAD-ul.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1589
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved