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.
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.
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.
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.
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.
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:
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.
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:
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
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.
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. |
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()
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 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:
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 |
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
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.
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.