Oxoscript se transforme en NanoPy - plus d'infos

Langage

Expressions

Les expressions sont des instructions qui permettent d’attribuer une valeur à des variables.

Dans le cas le plus simple, nous attribuons une valeur à une variable:

a = 10

Important: lors de la première affectation d’une valeur, on détermine le type de la variable. Si rien n’est indiqué, on utilise normalement “Int”. Ce type de données peut stocker un nombre de -32768 à 32767.

Si l’on programme à la place ce qui suit

a = 10.5

un autre type est utilisé. Dans ce cas, la variable est de type “float”. Ce type de données stocke un nombre dit à virgule flottante dans une plage de valeurs allant de 1.17549e-038 à 3.40282e+038. On appelle ce type de données ainsi parce que l’écriture exponentielle permet de représenter des nombres très grands et aussi très petits en déplaçant la virgule. La précision est toutefois limitée en raison du nombre restreint de chiffres.

Toutes les valeurs numériques peuvent être calculées à volonté

a = b / 5 -10

etc.

Il est également possible d’appeler des fonctions de la bibliothèque de fonctions, comme par exemple

a = sin(b) * 100

Calcule le sinus de la valeur du radian b et multiplie la valeur par 100.

En principe, nous appliquons la règle du point avant le tiret, comme nous l’avons appris en cours de mathématiques. On peut toutefois modifier l’ordre à l’aide de parenthèses:

a = (5 + sin(b)) * (100 - c)

Pour les affectations, on peut aussi utiliser des valeurs de tableau. Celles-ci sont indiquées avec l’index entre crochets:

somme = a[0] + a[1] + a[2] + a[3]

Si l’index du tableau se trouve à gauche de l’affectation, on peut modifier un élément du tableau:

a[i] = a[i+1]

Dans la mesure où les objets de classe contiennent des composants numériques, on peut les utiliser comme des variables normales. Ici, nous utilisons l’opérateur de point (“.”):

v1 = vector(x=1,y=1)
v2 = vector(x=2,y=2)
dotProduct = v1.x * v2.x + v1.y * v2.y

Outre les affectations simples avec le signe d’égalité “=”, il existe des variantes d’affectation supplémentaires:

”++”: Ce signe augmente la valeur de la variable de 1.

i=10
i++
i++
i++

“i” a maintenant la valeur 13.

”–”: réduit la valeur de 1.

”+=”: augmente la valeur de la variable de l’expression qui se trouve à droite.

i=10
i+= (100-95)*3

“i” a la valeur 25.

”-=”: réduit la valeur de l’expression à partir de la droite.

Constantes

Comme une variable, une constante est un caractère de remplacement que vous pouvez spécifier n’importe où dans le programme à la place d’une valeur numérique. Cependant, contrairement aux variables, la valeur d’une constante ne peut être définie qu’une seule fois et ne peut plus être modifiée.

Pour la déclarer, nous utilisons l’instruction “const”. Exemple :

const MAX_SIZE = 5.8

Pour faciliter l’identification des constantes dans le code, on les écrit généralement en majuscules et en snake-case (voir Conventions d’appellation).

Utilisation de l’éditeur de constantes :

Si l’on déclare les constantes selon le modèle suivant, elles apparaissent en outre dans l’éditeur de constantes sous forme de curseur ou de case à cocher :

const NOM = VALEUR # de la plage à la plage ...

Exemple :

const COLOR = 0 # 0 .. 255

Déclare une constante “COLOR” avec une valeur initiale de 0 et une plage de valeurs de 0 à 255. Ceci peut ensuite être réglé via le curseur.

const FAST_MODE = true # true, false

Déclare une constante booléenne qui peut être soit “true” (vraie) soit “false” (fausse). Dans l’éditeur de constantes, un simple interrupteur est dessiné ici, que l’on peut activer/désactiver en cliquant dessus.

Si une valeur de couleur doit être sélectionnée, “HUE” peut être utilisé :

const COLOR = 0 # HUE

Les constantes peuvent également contenir des calculs, à condition qu’ils se limitent à des valeurs numériques ou à d’autres constantes déclarées précédemment :

const WIDTH = 100
const HEIGHT = 80
const VOLUME = WIDHT * HEIGHT
const HALF_VOLUME = (WIDTH * HEIGHT) / 2

Note importante : Il doit y avoir au moins 8 espaces avant le caractère #, sinon l’éditeur de coût ne fonctionnera pas.

Fonction

Une fonction est une procédure qui effectue un calcul et renvoie une valeur.

La manière la plus simple de comparer cela est avec les fonctions mathématiques, par exemple les fonctions trigonométriques sinus, cosinus, racine carrée, etc. Par exemple, on peut attribuer une valeur à une variable pour déterminer la valeur du sinus:

unevaleur = sin(PI/2)

Le sinus de PI/2 est “1”. C’est-à-dire que la variable “uneValeur” aura la valeur 1 après cet appel.

Les fonctions peuvent également faire partie d’un objet. La classe “vector” contient la fonction “distance” qui permet de calculer la distance entre deux vecteurs.

Exemple:

v1 = vector(x=2,y=4)
v2 = vector(x=10,y=10)
d = v1.distance(v2)

“d” contient la valeur 10.

Il est possible de déclarer ses propres procédures et fonctions avec l’instruction “def”:

Déclaration d’une procédure sans valeur de retour:

def Nom de la procédure(paramètre1, paramètre...):
    # instructions...

Déclaration d’une fonction avec valeurs de retour:

def Nom de la fonction(paramètre1, paramètre ..)->Valeur de retour:
    # ....
    return valeur de retour

Exemples:

def drawHelloWorld():
    background(0,0,0)
    stroke(255,255,255)
    drawText(10,10, "Hello World !")
    update()

def drawMatrix(taille):
    for x in size:
        for y in size:
            drawRectangle(x,y,size,taille)

def diff(a:float,b:float)->float:
    if a>b:
        return a-b
    else
        return b-a

Toutes les fonctions exécutent les instructions en retrait. L’instruction “return” permet de quitter immédiatement la fonction. Si la fonction a défini une valeur de retour, il faut en plus indiquer une valeur pour “return”.

Ces fonctions définies peuvent être appelées de cette manière:

drawHelloWorld()

Dessine “Hello World !” sur l’écran.

drawMatrix(10)

Dessine 10 * 10 rectangles de 10 * 10 pixels.

d = diff(10.4,2.1)

Renvoie la différence entre les deux nombres (=8.3) et stocke la valeur dans la variable “d”. Nous voyons ici dans le code que “return” renvoie un calcul. Selon le nombre le plus grand, on renvoie soit le résultat du calcul de “a-b”, soit celui de “b-a”.

while loop

Avec “while”, un bloc d’instructions peut être répété jusqu’à ce qu’une condition ne soit plus remplie.

La structure est la suivante:

while condition:
    # instructions

“Condition” peut être une expression quelconque qui est soit vraie soit fausse. Tant que la condition est remplie, le bloc est exécuté à plusieurs reprises.

Exemple:

fond(0,0,0)
y=10
while y<240:
    drawLine(0,y,240,y)
    y = y + 10
update()

Cet exemple dessine une ligne verticale tous les 10 pixels.

Si la condition est toujours remplie, on parle de boucle infinie. Celle-ci peut par exemple être déclarée ainsi:

while true:
    background(0,0,0)
    update()
    delay(500)
    background(255,255,255)
    update()
    delay(500)

L’exemple fait s’allumer l’écran en noir et blanc sans fin.

Structure du programme

Un programme peut être construit de deux manières.

Par événement ou comme une suite d’instructions.

Dans l’approche événementielle, le programme réagit aux événements du système et s’exécute en principe sans fin.

Trois procédures événementielles sont disponibles: “onDraw()”, “onClick()” et “onTimer()”.
“onDraw()” est exécuté en permanence, chaque fois que le système est déchargé. Cela signifie que “onDraw()” est appelé plusieurs milliers de fois par seconde, selon la situation. Dans l’animation, cette fonction sert à dessiner et à actualiser l’écran. Mais on peut aussi l’utiliser à d’autres fins.

“onClick()” n’est appelé que lorsqu’un ou plusieurs boutons sont enfoncés. Cette fonction sert à interroger et à réagir aux clics.

Le système d’événements vérifie en permanence si un bouton est pressé. Si c’est le cas, après l’appel de “onDraw()”, “onClick()” est appelé. Les appels sont donc toujours séquentiels et non parallèles.

Structure générale:

# Déclaration des classes, des variables globales et des fonctions...

def onDraw():
    # Instructions...

def onClick():
    # instructions...

Exemple:

hue:int

def onDraw():
    backgroundHSV(hue,255,255)
    update()

def onClick():
    b:buttons = getButtons()
    if b.up: hue = 0
    if b.down: hue = 50
    if b.left: hue = 100
    if b.right: hue = 150
    if b.middle: hue = 255

L’exemple colore l’écran d’une couleur en fonction du bouton sur lequel on a appuyé.

Il est également possible de renoncer aux procédures événementielles et de formuler son programme comme une simple suite d’instructions. L’exemple suivant écrit “Hello World !” sur l’écran et se termine (s’arrête) ensuite. L’écran reste affiché jusqu’à ce que l’Oxocard reçoive un nouveau programme.

fond(0,0,0)
drawText(10,10, "Hello World !")
update()

Les deux variantes peuvent également être combinées à volonté, en tenant compte du fait que les appels définis globalement ne sont exécutés que lors du premier démarrage du script ou lors du redémarrage du script.

Exemple:

fond(0,0,0)
drawText(10,10, "Écrire ceci une fois")
flip:bool
noStroke()

def onDraw():
    flip = not flip
    if flip:
        fill(255,0,0)
    else:
        fill(0,255,0)
    drawRectangle(10,80,220,80)
    update()
    delay(500)

Les quatre premières lignes de ce script ne sont exécutées que lors du premier démarrage. Ensuite, le gestionnaire d’événements démarre et exécute indéfiniment “onDraw()”.

Variables

Une variable est un espace réservé pour une valeur. On peut aussi appeler une variable un “tiroir” pour lequel de la place est réservée dans la mémoire de l’ordinateur afin de stocker une valeur quelconque.

Chaque variable possède un type de données qui permet de fixer la taille et le type de contenu. Ces deux éléments ne peuvent plus être modifiés après avoir été spécifiés une fois. Cette convention est appelée “typage statique”. Dans les langages typés dynamiquement, les mêmes noms de variables peuvent être déclarés à nouveau, ce qui permet d’avoir d’autres contenus, mais malheureusement, de nombreuses erreurs peuvent aussi se glisser.

La déclaration d’une variable est nécessaire, c’est-à-dire qu’avant de l’utiliser, il faut la définir. Il existe ici deux possibilités:

  1. déclaration par l’attribution d’une valeur:
a = 10
b = true
c = 3.14
v = vector(x=10,y=20)

“a” est déclaré comme valeur int, b comme booléen, c comme float et v comme vector. La valeur détermine donc le type de données de la variable, sachant que pour les valeurs numériques, on utilise toujours au moins le type de données “int”.

  1. indication du type de données avec “:”.
a:int
b:byte
c:float
d:Rectangle

Tous les types de base et classes internes, ainsi que les classes personnalisées, peuvent être utilisés comme types de données.

Cas particulier des types de tableaux:

Si l’on souhaite obtenir plusieurs valeurs d’une variable, on peut indiquer le nombre de valeurs entre crochets.

points:int[10]

Ceci déclare une liste de points nommée “punkte”. La liste contient 10 entrées numérotées de 0 à 9.

Lors de l’attribution, il faut maintenant indiquer à chaque fois l’index, c’est-à-dire le numéro de l’entrée:

points[0] = 10
points[1] = 99
...

Le nombre d’éléments ne peut pas être modifié. Le nombre doit être un nombre, mais peut aussi être une constante:

const NOMBRE = 10
points:int[NOMBRE]

Tu trouveras plus d’informations sur les variables ici:

Opérations binaires

Il est également possible d’effectuer des opérations sur les bits sur Ganzahlen.

Les options suivantes sont disponibles:

nombre << n Déplace le nombre bit par bit de n chiffres vers la gauche
nombre >> n Déplace le nombre bit par bit de n chiffres vers la droite
nombre1 | nombre2 Concaténation logique "ou" des deux nombres
nombre1 & nombre2 Concaténation logique "et" des deux nombres
nombre1 ^ nombre2 Logique exclusive de la combinaison "ou" des deux nombres. nombres (XOR)

En décalant les nombres de n bits, le décalage vers la gauche correspond à une multiplication, le décalage vers la droite à une division par 2^n.

Quelques exemples:

0b00000001 << 1 = 0b00000010
0b10000000 >> 1 = 0b01000000
0b00000001 | 0b111111 = 0b111111
0b00000001 & 0b111111 = 0b00000001
0b00000001 ^ 0b111111 = 0b11111110

Systèmes de paiement

Dans le monde informatique, on utilise souvent le système décimal, mais aussi le système binaire et le système hexadécimal. Dans NanoPy, il est possible d’utiliser les trois systèmes.

En règle générale, nous utilisons le système décimal courant. Nous pouvons simplement attribuer les nombres comme valeur à une variable ou les utiliser dans des calculs:

a = 10
b = 4711
c = a / 5 - 10

La base de ces nombres est 10 et nous utilisons les “symboles” 0- 9.

Dans le système binaire, nous ne connaissons que 0 et 1. Dans NanoPy, les nombres binaires sont utilisés avec 0b.

a = 0b00001111
b = 0b0101

Il est également possible de calculer normalement avec des nombres binaires:

a = 10 + 0b00001111

Le troisième système de numération s’appelle l’hexadécimal. Ici, nous avons 16 symboles différents, qui est le système de seize. Nous complétons les chiffres de 0 à 9 par les lettres a - f (a=10, b=11, c = 12, etc.). Les nombres sont introduits par 0x:

a = 0xff
b = 0x10

Notez que 0x10 ne correspond PAS à la valeur décimale 10, mais à 16 !

Pour savoir comment les systèmes de numération sont convertibles entre eux, on peut consulter différentes sources sur youtube.

Types de données

Un type de données définit l’étendue de la valeur d’une variable. En mathématiques, nous connaissons les nombres réels, entiers, naturels, rationnels et irrationnels. En informatique, il existe encore d’autres ensembles de ce type et on peut aussi définir les siens.

Types de données intégrés:

Type de données plage de valeurs
byte 0 à 255
int -32768 à 32767
long -2147483648 à 2147483647
float 1.17549e-038 à 3.40282e+038
bool true ou false

Ces types de base peuvent être regroupés en compositions au moyen de classes. Par exemple, la classe vecteur (“vector”) contient deux variables de type “float” (x et y).

Les types de données intégrés suivants sont disponibles:

Type de données Description
vector Classe de vecteurs à deux dimensions avec des paramètres x et y (floats) et de nombreuses fonctions utiles de traitement des vecteurs.
vector3D Classe de vecteurs à trois dimensions avec des paramètres x, y et z (floats) et de nombreuses fonctions utiles de traitement des vecteurs.

Éditer des textes

L’Oxocard peut également afficher des textes de différentes tailles sur l’écran.

Il y a plusieurs options à choisir pour la sortie du texte.

Dans le cas le plus simple, est suffisant pour appeler drawText():

drawText(10,10, "Hello World !")
update()

Les textes peuvent également être composés d’autres textes ou de chiffres, comme le montre cet exemple :

nom = "Leo"
drawText(10,10, "Mon nom est " + nom)
update()

L’exemple suivant montre l’heure actuelle lorsque le programme est exécuté :

background(0,0,0)
textFont(FONT_ROBOTO_24)
drawText(10,10, "Il est " + getHour() + " :" + getMinute())
update()

for loop

La boucle for permet de parcourir une liste d’éléments.

La structure est la suivante:

for variable in expression:
    # instructions

“expression” peut être un nombre, une liste ou une variable.

Exemple 1:

push()
fond(0,0,0)
translate(120,120)
for i in 12:
    rotate(PI/6)
    drawCircle(80,0,10)
update()
pop()

Cet exemple dessine 12 cercles. Tout d’abord, le point d’origine est placé au centre de l’écran. A chaque passage de la boucle, le dessin est déplacé de 30 degrés dans le sens des aiguilles d’une montre et un cercle est dessiné.

Pour plus d’informations: voir Transformation de coordonnées et push() et pop().

La boucle peut aussi être initialisée avec une liste.

list = [1,20,80,100,80,20,1]
x = 20
fond(0,0,0)
fill(255,255,255)
for y dans list:
    drawRectangle(x,240-y,20,240)
    x = x+30
update()

Cet exemple dessine un diagramme à barres avec les valeurs de la liste qui ont été fixées ici.

L’exemple suivant initialise une liste de vecteurs:

vectors:vecteur[250]
for i in sizeof(vectors):
    vectors[i].random()
    vectors[i].mulScalar(random(5,180))

angle = 0.0
def onDraw():
    push()
    strokeWeight(5)
    translate(120,120)
    rotate(angle)
    fond(0,0,0)
    for i in sizeof(vectors):
        strokeHSV(255,255,i%255)
        drawLine(0,0,vectors[i].x,vectors[i].y)
    update()
    pop()
    angle = angle + 0.05

Cet exemple déclare une liste (tableau) de 250 vecteurs. Tous ces vecteurs sont initialisés avec une direction arbitraire et ont une longueur aléatoire comprise entre 5 et 180 pixels.

Dans la procédure d’événement “onDraw”, ces vecteurs sont maintenant dessinés comme des lignes à partir du centre de l’écran. Après chaque passage, le centre est tourné de 0,05 radian.

Les boucles For sont à chaque fois réalisées avec la fonction:

sizeof(vecteurs)

est initialisée. La fonction “sizeof(…)” peut être appliquée à toutes les listes et renvoie le nombre d’éléments - dans ce cas 250.

Instruction conditionnelle avec if/then/else

Les instructions conditionnelles sont formulées à l’aide de l’instruction if.
La structure est la suivante:

if Ici se trouve une condition:
    Voici les instructions qui seront exécutées,
    si la condition est remplie.
elif Ici se trouve une autre condition:
    si la deuxième condition est remplie, ce
    bloc est exécuté.
else (ou autre):
    Ici se trouvent les instructions qui sont exécutées alternativement.

Les parties elif/else sont facultatives et peuvent être omises. En outre, le bloc “elif” peut être indiqué plusieurs fois, le bloc “else” devant toujours se trouver à la fin (ou être omis).

Exemple:

def onDraw():
    background(0,0,0)
    r = random(0,5)
    if r == 0:
        drawText(10,10, "Zero")
    elif r == 1:
        drawText(10,10, "One")
    else:
        drawText(10,10, "Nothing")
    update()
    delay(1000)

Les opérateurs de comparaison suivants peuvent être utilisés:

</table> Les résultats des opérateurs de comparaison peuvent également être affectés à des variables. Numériquement, "false" correspond à la valeur 0 et "true" à la valeur 1. Nous verrons également les opérations de comparaison à côté de la condition dans le bloc while. L'opérateur "not" peut être placé devant une variable, comme le démontre l'exemple suivant: ~~~pythonp to_be = false for i in 10: background(0,0,0) to_be = not to_be if to_be: drawText(10,10, "to be") else: drawText(10,10, "not to be") update() delay(1000) ~~~
Opérateurs de comparaison</td> Description</td> </tr>
== est égal à
!= n'est pas égal
> plus grand que
>= plus grand ou égal à
< plus petit que
<= inférieur ou égal à
&& ou "and" Et
|| ou "or" Ou
not Non

Procédure

Une procédure est une sorte d’instruction que l’ordinateur exécute lorsqu’on donne le nom de la procédure sous la forme “prozedur()”. est indiqué. Les parenthèses indiquent à l’ordinateur que nous voulons appeler une procédure et doivent toujours être indiquées. Entre les parenthèses, il est parfois nécessaire d’indiquer des valeurs, appelées paramètres. On trouve dans la documentation quelles sont ces valeurs ou si elles sont nécessaires. Les paramètres doivent toujours être indiqués avec précision, sinon la procédure ne peut pas fonctionner correctement et une erreur s’affiche.

Exemple de procédure sans paramètres:

returnToMenu()

Cette fonction termine le programme en cours et affiche à nouveau le menu. Exemple de procédure avec des paramètres:

drawPixel(50,100)

Cette fonction dessine un pixel à la coordonnée x= 50, y= 100.

Les procédures peuvent également renvoyer des valeurs. On parle alors de fonction.

Dans l’Oxocard, il existe de nombreuses procédures et fonctions internes que l’on peut utiliser. En outre, il est possible de construire ses propres fonctions. Des fonctions de classe sont également possibles. Voir l’entrée “Fonction”.

Classe

Les classes permettent de créer de nouveaux types de données. Ce terme est issu de la programmation orientée objet, qui permet de regrouper des objets dans une classe.

Nous pouvons par exemple définir une classe Point3D, qui stocke une coordonnée tridimensionnelle contenant une valeur x, y et z. Cette classe peut être utilisée pour créer des objets de type “point”.

classe Point3D:
    x:float
    y:float
    z:float

    def draw():
        drawPixel(x+(z/2),y+(z/2))

La classe peut maintenant être utilisée comme un type de données normal:

p1:Point3D

p1 a maintenant trois variables intégrées x,y,z, que l’on peut définir et lire individuellement:

p1.x = 10
a = p1.y
etc...

Il est également possible d’utiliser l’initialisateur de classe:

p1 = Point3D(x=10,y=5,z=3)

Dans la documentation, on trouve une liste des classes internes qui sont déjà déclarées et peuvent être utilisées (“vector”, “color” et autres).
L’exemple suivant déclare une classe “Circle” (cercle). Ce qui est spécial ici, c’est que la variable de classe “pos” a de nouveau un type de classe.

classe Cercle:
    pos:vector
    size:int

    def init(x:int,y:int,s:int):
        pos.x = x
        pos.y = y
        size = s

    def draw():
        drawCircle(pos.x,pos.y,size)

Nous pouvons maintenant définir la variable Circle “c” et appeler les fonctions définies:

c:Cercle
c.init(120,120,10)
c.draw()

Structure générale:

class Noms de classe:
    Variable1
    Variable2
    Variable ...
    ...
    Fonction1
    Fonction2
    Fonction ...


Notes importantes:

Le concept orienté objet de l’héritage des variables et des fonctions n’est pas implémenté. C’est pourquoi les classes sont plutôt à qualifier de composants. De plus, il n’y a pas encore de constructeurs/déstructeurs dans la version préliminaire d’NanoPy.

Procédure d’événement

Une procédure événementielle est une fonction spéciale que l’on ne doit pas appeler soi-même, mais que le système appelle automatiquement à des moments précis. Nous utilisons principalement la fonction “onClick()”, qui est appelée par le système chaque fois qu’une touche est enfoncée, et “onDraw()”, que l’ordinateur appelle lorsqu’il n’y a rien d’autre à faire.