Oxoscript turns into NanoPy - more infos

Language

Expressions

Expressions are statements that allow you to assign a value to variables.

In the simplest case, we assign a value to a variable:

a = 10

Important: the first time a value is assigned, the type of the variable is determined. If nothing is specified, ``int’’ is normally used. This data type can store a number from -32768 to 32767.

If we program the following instead,

a = 10.5

a different type is used. In this case the variable has the type ``float’’. This datatype stores a so called floating point number in the value range from 1.17549e-038 to 3.40282e+038. This datatype is called this because you can represent very large and also very small numbers with the exponential notation by shifting the decimal point. However, the accuracy is limited because of the few digits.

With all numerical values can be calculated arbitrarily

a = b / 5 -10

etc.

It is also possible to call functions of the function library, such as:

a = sin(b) * 100

Calculates the sine of the radian value b and multiplies the value by 100.

Basically, we apply the dot-before-dash rule, as we know it from math class. However, you can change the order with parentheses:

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

For assignments, you can also use array values. These are specified with the index in square brackets:

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

If the array index is on the left side of the assignment, you can modify an array element:

a[i] = a[i+1]

Provided class objects contain numeric components, you can use them like normal variables. Here we use the dot operator (“.”):

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

Besides the simple assignments with the equal sign “=”, there are additional assignment variants:

”++”: This character increases the variable value by 1.

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

“i” now has the value 13.

”–”: reduces the value by 1.

”+=”: Increases the value of the variable by the expression to the right.

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

“i” has the value 25.

”-=”: reduces the value by the expression from the right.

Constants

Like a variable, a constant is a placeholder that can be specified anywhere in the programme instead of a numerical value. In contrast to variables, however, you can only define the value of a constant once and cannot change it again.

For the declaration we use the instruction “const”. Example:

const MAX_SIZE = 5.8

To make it easier to recognise constants in the code, they are usually written in capital letters and snake case (see naming conventions).

Use of the constant editor:

If you declare the constants according to the following pattern, they will also appear in the constant editor as a slider or checkbox:

const NAME = VALUE # from-range ... to-range

Example:

const COLOR = 0 # 0 .. 255

Declares a constant “COLOR” with an initial value of 0 and a range of values from 0 to 255. This can then be set via the slider.

const FAST_MODE = true # true, false

Declares a boolean constant that can be either “true” or “false”. In the constant editor, a simple switch is drawn here that can be switched on/off with a mouse click.

If a colour value is to be selected, “HUE” can be used:

const COLOR = 0 # HUE

Constants can also contain calculations, as long as they are limited to numerical values or other previously declared constants:

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

Important remark: There must be at least 8 spaces before the # character, otherwise the cost editor will not work.

Function

A function is a procedure that performs a calculation and returns a value.

The easiest way to compare this is with mathematical functions, for example the trigonometric functions sine, cosine, square root, etc. For example, you can assign a value to a variable determine sine value:

avalue = sin(PI/2)

The sine of PI/2 is “1”. I.e. the variable “einWert” will have the value 1 after this call.

Functions can also be part of an object. The class “vector” contains the function “distance”, which can be used to calculate the distance between two vectors.

Example:

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

“d” contains the value 10.

Custom procedures and functions can be declared using the “def” statement:

Declaration of a procedure without return values:

def procedure_name(parameter1, parameter...):
    # statements...

Declaration of a function with return values:

def function_name(parameter1, parameter ..)->returnvalue:
    # ...
    return Return value

Examples:

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

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

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

All functions execute the indent statements. With the statement “return” you can leave the function immediately. If the function has defined a return value, a value must also be specified with “return”.

These defined functions can be called like this:

drawHelloWorld()

Draws “Hello World!” on the screen.

drawMatrix(10)

Draws 10 * 10 rectangles of size 10 * 10 pixels.

d = diff(10.4,2.1)

Returns the difference of the two numbers (=8.3) and stores the value in the variable “d”. We see here in the code that “return” returns a calculation. Depending on which number is greater, either the result of the calculation of “a-b” or “b-a” is returned.

while loop

With “while” a block of statements can be repeated until a condition is no longer fulfilled.

The structure is as follows:

while condition:
    # statements

“condition” can be any expression that is either True or False. As long as the condition is true, the block will keep executing.

Example:

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

This example draws a vertical line every 10 pixels.

If the condition is always met, it is called an infinite loop. This can be declared like this, for example:

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

The example makes the screen light up black and white endlessly.

Program structure

A program can be built in two ways.

Event-driven or as a sequence of statements.

In the event-driven approach, the program reacts to system events and basically runs endlessly.

The three event procedures: “onDraw()”, “onClick()” and “onTimer()” are available.
“onDraw()” is executed continuously, whenever the system is unloaded. This means that “onDraw()” is called several thousand times per second, depending on the situation. The function is used in animation as a drawing function that updates the screen. However, you can also use it for other purposes.

“onClick()” is only called when one or more buttons are pressed. This function is used to query and react to clicks.

“onTimer()” is only called when a previously defined timer is triggered.

The event system continuously checks if a button is pressed or a timer is triggered. If it is, “onClick()” or “onTimer()” is called after the call to “onDraw()”. So the calls are always sequential, not parallel.

General structure:

# Declaration of classes, global variables and functions...

def onDraw():
    # instructions...

def onClick():
    # instructions...

def onTimer():
    # instructions...

Example:

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

The example colors the screen with a color depending on which button was pressed.

Alternatively, you can dispense with event procedures and formulate your program as a simple sequence of statements. The following example writes “Hello World!” to the screen and then terminates (stops). The display remains until the Oxocard receives a new program.

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

Both variants can also be combined arbitrarily, whereby one must note that globally defined calls are only executed when the script is started for the first time or when the script is restarted.

Example:

background(0,0,0)
drawText(10,10, "Write this once")
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)

The first four lines of this script will only be executed the first time it is started. After that the event control starts, which executes “onDraw()” endlessly.

Variables

A variable is a placeholder for a value. You can also call a variable a “drawer” for which space is reserved in the computer’s memory to store some value.

Each variable has a data type that is used to fix the size and also the type of content. Neither can be changed once specified. This convention is called “statically typed”. In dynamically typed languages the same variable names can be declared again and again, whereby also other contents are possible, but unfortunately also many errors can creep in.

The declaration of a variable is necessary, i.e. before you use it, you have to define it. There are two possibilities here:

  1. declaration by assigning a value:
a = 10
b = true
c = 3.14
v = vector(x=10,y=20)

“a” is declared as an int value, b as a boolean, c as a float, and v as a vector. The value thus determines the data type of the variable, whereby at least the data type “int” is always used for numeric values.

  1. specification of the data type with “:”
a:int
b:byte
c:float
d:rectangle

All internal base types and classes, as well as your own classes, can be used as data types.

Special case array types:

If you want to have multiple values of a variable, you can specify the number of values in square brackets.

points:int[10]

This declares a list of points named “points”. The list contains 10 entries, numbered from 0 to 9.

In the assignment, the index, i.e. the number of the entry, must now be specified in each case:

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

The number of elements cannot be changed. The number must be a number, but can also be a constant:

const NUMBER = 10
points:int[NUMBER]

You can find more information about variables here:

Binary operations

Bit operations can also be performed on whole numbers.

The following are available:

number << n Shifts the number bitwise n places to the left
number >> n Shifts the number bitwise by n places to the right
number1 | number1 Logical OR operation of the two numbers
number1 & number2 Logical ampersand operation of the two numbers
number1 ^ number2 Logical exclusive OR-connection of the two Numbers (XOR)

When shifting the numbers by n bits, shifting left corresponds to multiplication, and shifting right corresponds to division by 2^n.

Some examples:

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

Number systems

In the computer world, the binary and hexadecimal systems are often used in addition to the decimal system. In NanoPy you can use all three systems.

Normally we use the common decimal system. We can simply assign the numbers as values to a variable or use them in calculations:

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

The base of these numbers is 10 and we use the “symbols” 0- 9.

In the binary system, we only know 0 and 1. In NanoPy, the binary numbers are used with 0b.

a = 0b00001111
b = 0b0101

Binary numbers can also be used for normal arithmetic:

a = 10 + 0b00001111

The third number system is called hexadecimal. Here we have 16 different symbols which this is the sixteen system. We add the letters a - f (a=10, b=11, c = 12 etc.) to the numbers 0 to 9. The numbers are introduced with 0x:

a = 0xff
b = 0x10

Note that 0x10 does NOT correspond to the decimal value 10, but 16!

How the number systems are convertible to each other can be found on youtube from various sources.

Data types

A data type defines the value range of a variable. In mathematics we know the real, integer, natural, rational and irrational numbers. In computer science there are other such sets and you can also define your own.

Built-in data types:

data type value ranges
byte 0 to 255
int -32768 to 32767
long -2147483648 to 2147483647
float 1.17549e-038 to 3.40282e+038
bool true or false

These basic types can be combined into compositions using classes. For example, the vector class (“vector”) contains two variables of type “float” (x and y).

The following built-in data types are available:

data type description
vector Two-dimensional vector class with x- and y- parmeters (floats) and many useful vector manipulation functions.
vector3D Three-dimensional vector class with x-, y- and z- parmeters (floats) and many useful vector manipulation functions.

Output texts

The Oxocard can also display texts in different sizes on the screen.

There are several options to choose from for text output.

In the simplest case, is sufficient to call drawText():

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

Texts can also be composed of other texts or numbers, as this example shows:

name = "Leo"
drawText(10,10,"My name is " + name)
update()

The following example shows the current time when the program is executed:

background(0,0,0)
textFont(FONT_ROBOTO_24)
drawText(10,10,"It is " + getHour() + ":" + getMinute())
update()

for loop

You can use the for loop to go through a list of items.

The structure is as follows:

for variable in expression:
    # statements

“expression” can be a number, a list, or a variable.

Example 1:

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

This example draws 12 circles. First, the origin is placed at the center of the screen. With each pass of the loop, the drawing is shifted 30 degrees clockwise and a circle is drawn.

More info: see coordinate transformation and push() and pop().

The loop can also be initialized with a list.

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

This example draws a bar chart with the values of the list specified here fixed.

The following example initializes a list of vectors:

vectors:vector[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)
    background(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

This example declares a list (array) of 250 vectors. All these vectors are initialized with an arbitrary direction and have a random length between 5 and 180 pixels.

In the event procedure “onDraw”, these vectors are now drawn as lines from the center of the screen. After each pass, the center point is rotated by 0.05 radians.

The for-loops are each performed with the function:

sizeof(vectors)

initialized. The function “sizeof(…)” can be applied to all lists and returns the number of elements - in this case 250.

Conditional statement with if/then/else

Conditional statements are formulated with the if statement.
The structure is as follows:

if Here is a condition:
    Here are the statements that will be executed,
    if the condition is met.
elif Here is another condition:
    if the second condition is met, this
    block is executed.
else:
    Here are the statements that will be executed alternatively.

The elif/else parts are optional and can be omitted. In addition, the “elif” block can be specified multiple times, but the “else” block must always be at the end (or omitted).

Example:

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)

The following comparison operators can be used:

</table> Results of comparison operators can also be assigned to variables. Numerically, "false" corresponds to the value 0 and "true" to the value 1. We will see the comparison operations next to-condition also in the while block. The "not" operator can be prepended to a variable, as demonstrated in the following example: ~~~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) ~~~
comparison operators</td> Description</td> </tr>
== is equal
!= is not equal
> greater than
>= greater than or equal to
< less than
<= less than or equal to
&& or "and" And
|| or "or" Or
not Not

Procedure

A procedure is a type of command statement that the computer executes when you specify the procedure name in the form “procedure()”. . The parentheses tell the computer that we want to call a procedure and must always be specified. Between the brackets, there are sometimes values that you need to specify, called parameters. What these are, or whether they are necessary, can be found in the documentation. The parameters must always be specified exactly, otherwise the procedure cannot function properly and an error is displayed.

Example of a procedure without parameters:

returnToMenu()

This function exits the current program and displays the menu again. Example of a procedure with parameters:

drawPixel(50,100)

This function draw a pixel at coordinate x= 50, y= 100.

Procedures can also return values. Then one speaks of a function.

With the Oxocard, there are many internal procedures and functions that you can use. In addition, you can also build your own functions. Class functions are also possible. See entry “Function

Class

New data types can be created with classes. The term comes from object-oriented programming, where things that belong together can be combined into a group - a class.

For example, we can define a class Point3D, which stores a 3-dimensional coordinate that contains an x,y and z value.

class Point3D:
    x:float
    y:float
    z:float

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

The class can now be used like a normal data type:

p1:Point3D

p1 now has three built-in variables x,y,z that can be set and read individually:

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

Alternatively, the class initializer can be used:

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

See the documentation for a list of internal classes that are already declared ready to use (“vector”, “color”, and others).
The following example declares a class “Circle”. Special here is that the class variable “pos” has again a class type.

class Circle:
    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)

We can now define the Circle variable “c” and call the defined functions:

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

General structure:

class Class name:
    Variable1
    Variable2
    Variable...
    ...
    Function1
    Function2
    Function...


Important Notes:

The object-oriented concept of inheritance of variables and functions is not implemented. Therefore, the classes are rather to be described as components. There are currently also no constructors / destructors in the preliminary version of NanoPy.

Event procedure

An event procedure is a special function that you don’t have to call yourself, but is called automatically by the system at certain times. We mainly use the function “onClick()” which is called by the system whenever a key is pressed and “onDraw()” which is called by the computer when there is nothing else to do.