Nacházíte se zde: Rhino3D.cz > Články > Rhinoceros > RhinoScript 8. dil: Uživatelské funkce


RhinoScript 8. dil: Uživatelské funkce

Publikováno: 29.8.2005 | Autor: David Rutten | Rubrika: Rhinoceros | Komentáře: 1 - Doporučit

Rhino Script - úvodVíteje v poslední kapitole této příručky. Probereme zde poslední záležitosti, které musíte znát, než budete schopni naprogramovat pro Rhino téměř cokoliv, co si přejete. Již víte, že se téměř všechny naše 'akce’ odehrávají v procedurách a funkcích. Také víte, že vbScript obsahuje množství předdefinovaných funkcí jako Sin(), Cos(), Left(), UCase() atd.

8.1    Procedury a funkce

Víteje vposlední kapitole této příručky. Probereme zde poslední záležitosti, které musíte znát, než budete schopni naprogramovat téměř cokoliv, co si přejete. Již víte, že se téměř všechny naše 'akce’ odehrávají v procedurách a funkcích. Také víte, že vbScript obsahuje množství předdefinovaných funkcí jako Sin(), Cos(), Left(), UCase() atd.

Nyní se dozvíte, jak vytvořit své vlastní funkce...

8.2.1 Jak funkce pracují

Funkce (nebo procedura) je kus kódu, který je vykonán z nějakého jiného místa. Funkcím můžete předat argumenty a funkce mohou provádět akce nebo vracet hodnoty nebo obojí. Do funkcí často vkládáme algoritmy. Algoritmy jsou řešitelé poblémů.

Často potřebujeme provést určité operace několikrát. Někdy je nejlepším řešením cyklus, ale jindy používáme ve funkcích algoritmy, abychom zachovali náš kód malý a čitelný.

Bez okolků si ukážeme si první funkci:

01   Function VypoctiProstredniBod(arrBod1, arrBod2)
02      Dim arrProstredniBod(2)
03      Dim i
04   
05      For i = 0 To 2
06         arrProstredniBod(i) = (arrBod1(i)+arrBod2(i)) / 2
07      Next
08   
09      VypoctiProstredniBod = arrProstredniBod
10   End Function

Tato konkrétní funkce je navržena pro nalezení bodu v polovině mezi dvěma zadanými body. V podstatě jsme použili téměř identický kód jako v dlouhém skriptu na konci minulého dílu. Procedury a funkce nikdy nejsou navzájem zahnízděny. Nelze je zahnízdit jako podmínky If..Then nebo jako cykly. Vždy jsou ve skriptu řazeny sekvenčně za sebou.

Tato funkce má název VypoctiProstredniBod() a má dva argumenty (arrBod1 a arrBod2). Oba argumenty musí být numerická pole, každé se 3 prvky, jinak dojde ke zhroucení funkce (nepoužili jsme žádné odchytávání chyb). Funkce vrátí jedno numerické pole, také se 3 prvky. Pokud bychom chtěli zavolat tuto funkci zevnitř procedury Main(), museli bychom přidat následující řádek kódu:

    Dim arrProstredniBod
    arrProstredniBod = VypoctiProstredniBod(arrPrvniBod, arrDruhyBod)

Proměnná před znaménkem = bude naplněna daty, vrácenými funkcí. V našem případě to bude numerické pole se 3 prvky.
Jak vidíte, není žádný rozdíl v použití nativních a uživatelských funkcí. Syntaxe pro obě je zcela identická.

Dalším zajímavým faktem je, že můžeme použít více než jeden argument. Zde používáme dva argumenty, ale tento počet není omezen. Mnoho nativních funkcí a metod Rhina také přijímá či dokonce vyžaduje více argumentů. Vjednom zpředchozích dílů jsme probrali funkci Left(). Kdykoliv chcete použít tuto funkci, musíte zadat řetězec a hodnotu typu Long. Funkce vrátí řetězec, který je identický se zadaným, ale bude zkrácen na zadanou délku.

Daleko složitějším příkladem je metodaRhino.GetObjects() . Tuto mehodu jsme již dříve použili, ale vždy s jedním argumentem. Pokud si tuto metodu vyhledáte v nápovědě RhinoScriptu, uvidíte následující řádek:

     Rhino.GetObjects ([strMessage [, intType [, blnGroup [,
                                                blnPreSelect [, blnSelect [, arrObjects ]]]]])

To je syntaxe této konkrétní metody. Jak vidíte, můžeme použít až 6 argumentů a žádný není povinný. Argumenty mezi závorkami [ ] jsou vždy volitelné.

strMessage
Zde vložíme text, který chceme zobrazit v příkazovém řádku.

intType
Zde definujeme, jaký druh objektu akceptujeme. Pouze křivky nebo jen body, nebo pouze spojené plochy a obyčejné plochy...

blnGroup
Zde definujeme, zda budou nebo nebudou vybrány skupiny, pokud bude vybrán jeden objekt z této skupiny.

blnPreselect
Zde definujeme, zda se budou brát do úvahy již vybrané objekty.

blnSelect
Zde definujeme, zda budou zvolené objekty vybrány.

arrObjects
A konečně můžeme specifikovat pole s identifikátory objektů, pokud chceme omezit výběr na určité objekty.

Pokud tedy chceme, aby uživatel vybral pouze křivky a mraky bodů, chceme akceptovat předem vybrané objekty a nechceme aby uživatel vybíral skupiny, obsahoval by náš skript následující řádek:

            arrObj = Rhino.GetObjects("Pick curves", 4+2, vbFalse, vbTrue, vbFalse)

 

8.3    Další informace

Měli byste vědět pár důležitých věcí, které se týkají použití funkcí. Jejich chování bohužel není vždy logické. Až dosud jsme k předávání argumentů funkcím používali závorky:

            arrObjekty = Rhino.GetObjects("Vyberte křivky")

Závorky jsou ale povoleny pouze tehdy, když funkce vrací hodnotu. Pokud zavoláme funkci aniž bychom zpětně obdrželi nějaká data, nemůžeme závorky použít:

Špatně!     Rhino.AddPoint (arrSouradniceBodu)
Správně     Rhino.AddPoint arrSouradniceBodu
Správně     strPointID = Rhino.AddPoint(arrSouradniceBodu)

Když vytvoříme vlastní funkce (uživatelské funkce), nemůžeme přidávat volitelné argumenty. Pouze nativní funkce vbScriptu a metody Rhina mohou mít volitelné argumenty.

01  Sub Main()
02   Dim blnResult
03   blnResult = DisplayCopyRightMessage("Reinier a Carl", vbTrue)
04  End Sub
05  
06     Funkce DisplayCopyrightMessage(strNazvy, blnPouzitPrikazovyRadek)
07     Dim strZprava
08  
09     strZprava =  "Tento skript napsala autorská práva vlastní " & _
10                  strNazvy & "." & vbNewLine & _
11                  "Smíte používat a upravovat tento kód " & _
12                  "ale nesmíte odstranit copyright."
13  
14     If blnPouzitPrikazovyRadek Then
15        Rhino.Print strZprava
16     Else
17        Rhino.MessageBox strZprava, 64, "info o copyrightu"
18     End If
19  
20     DisplayCopyRightMessage = vbTrue
21  End Function

Funkci můžete ukončit kdykoliv pomocí příkazu Exit Function. Všechny akce budou ukončeny a funkce vrátí řízení funkci nebo proceduře, která ji zavolala. Kdykoliv zastavíte funkci, ať už pomocí End Function nebo Exit Function, vrátí proměnnou, kterou jste přiřadili jejímu názvu. V předchozí funkci jsme přiřadili hodnotu funkci na řádku 21. Můžeme to bezpečně udělat, protože nikde není příkaz Exit Function a proto budou vždy vykonány všechny řádky. Jakmile však začneme používat odchytávání chyb a příkazy Exit Function, začne být nezbytné, abyste se ujistili, zda vaše funkce vždy vrátí proměnnou.

Jak si možná pamatujete, metody Rhina vždy vrací proměnnou vbNull, pokud se přihodí něco špatného. Abychom to implementovali do našich funkcí, provedeme následující:

1     Function FixStringLength(strZaklad, lngDelka)
2      FixStringLength = vbNull
3   
4      If Not IsNumeric(lngDelka) Then Exit Function
5      If VarType(strZaklad) <> vbString Then Exit Function
6   
7      lngDelka = Fix(lngDelka)
8      If lngDelka < 1 Then
9         FixStringLength = ""
10        Exit Function
11     End If
12  
13     strZaklad = strZaklad & Space(lngDelka)
14     strZaklad = Left(strZaklad, lngDelka)
15  
16     FixStringLength = strZaklad
17  End Function

Tato funkce má neprůstřelný systém odchytávání chyb. Jakmile funkce začne (řádek 2), okamžitě nastavíme proměnnou s názvem funkce na vbNull. Nyní, pokud ukončíme funkci, bude vrácena hodnotavbNull.
Všimněte si, že tato funkce neinicializuje žádné proměnné. Ale to nutně neznamená, že bychom je nepoužívali. Tato funkce má v podstatě 3 proměnné dokonce ještě před tím,  než začne běžet. Název funkce (FixStringLength) je ždy automaticky proměnná, stejně jako její argumenty (strZaklad a lngDelka).

Nebudeme si už dělat analýzu řádek po řádku, protože to byste již měli být nyní schopni provést sami. Zmíním však všechny nové věci...

Řádek 4:        IsNumeric() určí, za proměnná může být Rhinem vyhodnocena jako číslo. Také řetězce, které obsahují pouze čísla, lze načíst jako čísla.
Řádek 7:        Fix() přemění jakékoliv číslo na celé číslo... a na nic se neptá.
Řádek 9:         "" znamená prázdný řetězec.
Řádek 13:       Space() vytvoří řetězec se zadaným počtem mezer.

Tato funkce tedy převede každý řetězec na řetězec pevné délky. Můžete zadat délku pomocí hodnoty Double nebo Long. Double budou převedeny na celá čísla. Chybějící znaky (pokud je řetězec příliš krátký) budou zaplněny mezerami.

 

8.4    Čas cvičení

Nyní byste již měli být schopni sami některé skripty sami. Problém se skutečnou prací je ten, že je obvykle velice složitá. Proto jsem začlenil malé úkoly sjednoduchými problémy. Cílem každého úkolu je napsat funkci, která provede určitou akci nebo vypočítá určité řešení. Radím vám, abyste svůj postup konzultovali s touto příručkou, s nápovědou vbScriptu a s nápovědou RhinoScriptu.

Úkol 1:   Práce s booleovskými proměnnými
Vytvořte funkci, která vypočítá většinu hlasů ve struktuře se 3
hlasy. Většina se skládá ze 2 nebo 3 identických booleovských hodnot.
Vstup: 3 booleovské proměnné
Výstup: 1 booleovská proměnná nebo vbNull nebo chyba
Pokuste se vejít do 30 řádků kódu.

Úkol 2:   Práce s čísly
Vytvořte funkci, která zkontroluje, zda je numerická proměnná
součástí multiplikační tabulky jiné numerické proměnné.
Proměnná 1 musí tedy být celočíselným dělitelem proměnné 2.
Vstup: 2 numerické proměnné
Výstup: 1 booleovská proměnná nebo vbNull nebo chyba
Pokuste se vejít do 30 řádků kódu...

Úkol 3:   Práce s řetězci
Vytvořte funkci, která odstraní každý druhý znak ze řetězce.
"aAbBcCdDeEfF" se tak stane "abcdef" a "0123456789" se stane"02468".
Vstup: 1 proměnná typu řetězec
Výstup: 1 proměnná typu řetězec nebo vbNull nebo chyba
Pokuste se vejít do 40 řádků kódu...

Úkol 4:   Práce s Rhinem
Vytvořte skript, který umožní vstup nekonečného počtu bodů (vybraných myší
nebo již existujících bodů... volba je na vás) a který vypočítá průměrné souřadnice všech těchto bodů.

Pro „komplikované“ matematické záležitosti zkuste použít funkce. Také nezapomeňte přidat co nejvíce odchytávání chyb.
Než začnete kódovat, udělejte si plán (v češtině) a ujistěte se, zda tento plán funguje...

prumerne souradnice

 

Úkol 1, možné řešení

01     Function Vetsina(blnHlas1, blnHlas2, blnHlas3)
02     Vetsina = vbNull              'Dáme funkci návratovou hodnotu
03     Dim blnMajorityResult       'Deklarujeme booleovskou proměnnou
04   
05     If VarType(blnHlas1) <> vbBoolean Then Exit Function
06     If VarType(blnHlas2) <> vbBoolean Then Exit Function
07     If VarType(blnHlas3) <> vbBoolean Then Exit Function
08                             'Zajistili jsme korektní vstup
09     blnMajorityResult = vbFalse
10                            'Nastavili jsme výchozí výsledek na FALSE
11    If (blnHlas1 And blnHlas2) Or (blnHlas2 And blnHlas3) Or _
12       (blnHlas1 And blnHlas3) Then
13                           'Určíme zda alespoň 2 ze 3 hodnot
14                            'jsou TRUE. Pokud nebude se žádný ze členů
15                            'vyhodnocen jako TRUE, bude kód uvnitř
16                            'konstrukce If..Then přeskočen
17                           
18      blnMajorityResult = vbTrue
19                            'Změna výsledku na TRUE
20    End If
21 End Function

Tato funkce má neprůstřelný systém odchytávání chyb. Prvně zajistíme, že procedura nebo funkce, která zavolala tuto funkci, dodala 3 booleovské hodnoty. Pokud se ukáže, že jedna ztěchto hodnot je něco jiného, ukončíme funkci a vrátíme hodnotu vbNull .

Poté budeme předpokládat, že výsledek bude vbFalse. Samozřejmě, pokud nejméně 2 ze 3 hodnot budou pravdivé, pak celá kosntrukce:

     (blnHlas1 And blnHlas2) Or (blnHlas2 And blnHlas3) Or _
     (blnHlas1 And blnHlas3)

bude vyhodnocena jako TRUE. Vtakovém případě bude vykonán  blok kódu v příkazu If..Then. Zde změníme výslednou hodnotu funkce na vbTrue.

Také si všimněte, že jsem použil podtržítko pro prodloužení řádku kódu. Enter obvykle označuje konec řádku. Několik řádků však můžeme „sešít“ dohromady pomocí podtržítek. To je užitečné, když jsou řádky kódu širší než obrazovka.

Tato funkce je čitelná, ale také velice neefektivní. V podstatě, pokud rezignujeme na odchytávání chyb, můžeme ji napsat do jednoho řádku:

1     Function Vetsina(Hlas1, Hlas2, Hlas3)
2      Vetsina = (Hlas1 And Hlas2) Or (Hlas2 And Hlas3) Or (Hlas1 And Hlas3)
3     End Function

 

Úkol 2, možné řešení

1     Function JeDelitelne(numKontrola, numTabulka)
2     JeDelitelne = vbNull
3                             'Dáme funkci návratovou hodnotu
4   
5     If Not IsNumeric(numKontrola) Then Exit Function
6     If Not IsNumeric(numTabulka) Then Exit Function
7     If numTabulka = 0 Then Exit Function
8                             'Zajistili jsme korektní vstup.
9     If (numKontrola / numTabulka) = CLng(numKontrola / numTabulka) Then
10      JeDelitelne = vbTrue
11    Else
12      JeDelitelne = vbFalse
13    End If
14 End Function

Tento příklad není úplně neprůstřelný. Nejprve by měl akceptovat všechny typy numerických proměnných. Mezi ně patří Long a Double, ale také jiné typy numerických proměnných, o kterých jsme se nebavili, jako je Byte, Integer, Single a Currency. Vlastně by měl pracovat i s numerickými řetězci, jako "3.256" nebo "-9805".
Kontrola, zda patří vstupní hodnoty k *jakémukoliv* z těchto typů by zabrala mnoho řádků. Místo toho je snadnější použít funkci IsNumeric().

Dále nemá smysl dávat hodnotu 0 (nula) jako hodnotu numTabulka . Také musíme pro tento případ přidat odchytávání chyb.

Abychom zkontolovali, zda je číslo částí multiplikačních tabulek jiných čísel, podíváme se, zda je výsledkem dělení celé číslo. Provedeme to nejsnadněji tak, že porovnáme toto dělení s Long verzí sebe sama. Měli bychom přelstít strukturu If..Then použitím funkceCBool():

      JeDelitelne = CBool((numKontrola / numTabulka) = CLng(numKontrola / numTabulka))

Opět, pokud bychom si mohli dovolit ignorovat odchytávání chyb, tato funkce by se vešla do jediného řádku...

 

Úkol 3, možné řešení

01     Function SliceString(strVstup)
02     SliceString = vbNull
03     If VarType(strVstup) <> vbString Then Exit Function
04                             'Zkontrolujeme platný vstup
05     SliceString = strVstup
06     If Len(strVstup) <= 1 Then Exit Function
07                             'Kontrola, zda je řetězec dost dlouhý
08     Dim strOutput
09     Dim i
10    strOutput = ""
11                             'Inicializace proměnných
12    For i = 1 To Len(strVstup) Step 2
13        strOutput = strOutput & Mid(strVstup, i, 1)
14    Next
15
16    SliceString = strOutput
17        End Function

To je celkem jasné. Pro vykonání této úlohy potřebujeme cyklus, protože zadaný řetězec může mít jakoukoliv délku. Dále také potřebujeme funkci, která dokáže vyjmout zřetězce jeden znak. Udělá to funkce Mid().

Nad rámec Mid() potřebujeme také funkciLen(), která vrací délku řetězce. Potřebujeme vědět, kolikrát máme cyklus vykonat.

Možná jste si všimnuli, že před příkazy pro deklaraci proměnných je v této funkci hodně kódu. Myšlenka toho je následující: pokud ukončíme funkci kvůli chybnému vstupu, nebudeme potřebovat deklarovat proměnné. Z důvodu optimalizace kódu je vždy nejlepší předejít věcem, které nejsou nezbytné.

 

Úkol 4, možné řešení

Plán v krocích

A          Vyzveme uživatele kzadání bodů.
B         Ujistíme se, zda uživatel provedl, oč byl žádán.
C         Zahájíme cyklus pro každý bod
D         Získáme souřadnice (x,y,z) každého bodu
E         Přidáme každou souřadnici khodnotě sumy
F          Vrátíme se na začátek cyklu
G         Podělíme všechny sumy počtem bodů
H         Vytiskneme průměrnou souřadnici do příkazového řádku
I          Do modelu přidáme textové kolečko

Celý skript

01    Option Explicit
02    'Script napsal Gelfling '04      07-07-2004
03    Sub Main()
04     Dim arrObjekty, arrSouradnice
05     Dim arrPrumer(2)
06     Dim i, sumaX, sumaY, sumaZ
07     arrObjekty = Rhino.GetObjects("Vyberte body",1,,vbTrue)
08     If IsNull(arrObjekty) Then Exit Sub
09     sumaX = 0
10    sumaY = 0
11    sumaZ = 0
12
13    For i = 0 To Ubound(arrObjekty)
14      arrSouradnice = Rhino.PointCoordinates(arrObjekty(i))
15      sumaX = sumaX + arrSouradnice(0)
16      sumaY = sumaY + arrSouradnice(1)
17      sumaZ = sumaZ + arrSouradnice(2)
18    Next
19
20    arrPrumer(0) = sumaX / (UBound(arrObjekty)+1)
21    arrPrumer(1) = sumaY / (UBound(arrObjekty)+1)
22    arrPrumer(2) = sumaZ / (UBound(arrObjekty)+1)
23
24    Rhino.Print "Průměr: " & Rhino.Pt2Str(arrPrumer, 5)
25    Rhino.AddTextDot "Těžiště", arrPrumer
26   End Sub
27
28   Main

Krátké, efektivní, čisté, čitelné. Použili jsme více proměnných než je nutné, více řádků než je nutné, ale jedinou chybou v tomto skriptu je, že postrádá komentáře.

Také by bylo lepší vložit sekci s cyklem do samostatné, uživatelské funkce místo toho, abychom ji vkádali do procedury Main() .

Nahoru ↑

Diskuse k článku

  • [1] JJJ – 15. 09. 2007, 11:49

    reagovat

    Na tento dil o Rhino scriptu chybi odkazy v ostatnich clancich