Actions

Difference between revisions of "Freecode graphics"

From Future Skill

 
(57 intermediate revisions by 5 users not shown)
Line 1: Line 1:
 
=Graphics utility functionality in Freecode creator=
 
=Graphics utility functionality in Freecode creator=
==For executor version 3==
+
==Enable graphics and animations==
Before you start working go to the settings tab in the Freecode creator and check that your executor version is correct. This guide is created for executor version 3, but some parts might be applicable for other versions as well.
+
In the Freecode creator there is a page with settings for graphics, in this tab you have to enable "Uses canvas" in order to enable the canvas where graphics are displayed. In order to use animations you also have to check "has Canvas animation". Without the second option the canvas will only display 1 static update when running the code.
==Configuring the canvas==
 
Configuration of the canvas is done through the settings in the Freecode creator. In the graphics there is an option to determine if the implementation should make use of multiple steps or a single step. If you plan on making multiple graphics updates then make sure this option is checked. There is also a background color option which determines what the default background color of the canvas will be. Lastly there is also a resolution option, which determines both the maximum indexes available for placing graphics (possible to use decimals), and what dimensions the canvas will have. If making use of multiple steps, the step time is further used to determine the time spent on each step.
 
Setting up the API
 
In the Freecode creator there is an API tab, where one can add, edit and remove API methods. For it to be possible for the implementation to call a method in the solution, or for a solution to call a method in the Implementation through the API, those methods must be defined here. The comments and information added here will be available to anyone taking part of your challenge or exercise. To see previews of how it will look for them you can look in the preview tab. In order for the solution and implementation to remain compatible with any changes to the names of methods, arguments, or types, all those changes have to be made through this page. Of course you can create any number of methods that are not defined here, but you will only be able to call them internally in that file.
 
==The solution==
 
In the Freecode creator there is a solution tab, this is so you will have the possibility of creating a default solution used for verifying that the implementation works. When a user will attempt to make their own solution, they will be given the boiler plate code you can see in the preview tab.
 
  
==The implementation==
+
==Accessing the canvas object==
In the implementation tab you can set up the actual code that is used to run the challenge/exercise. The behaviour of the different levels are defined, the canvas data is configured, and scores are set here.
+
The canvas object which is used to create graphical objects is part of the context. To create a simple red circle placed 5 units into the canvas, sized 2 canvas units, one can use
==Printing to the console==
 
===Print from the solution===
 
To print from the solution to the console just use the regular print statement used by your language. In python3 it would be "print('hello world')".
 
===Print from the implementation===
 
In order to write to the console from the implementation one has to access either the console from the context, or one can access a console linked to a specific solution. Printing to specific solutions can be useful when creating a tournament where multiple solutions compete against each other.
 
  
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
  <nowiki>
 
  <nowiki>
self._context.console.log('Everyone can always see this')
+
self._context.canvas.new_circle(2, 'red', x=5, y=5)</nowiki>
 
 
self._context.console.debug('Everyone can see this during development')
 
 
 
solution.console.log('Only the tagged solution can see this')
 
 
 
solution.console.debug('The tagged solution can only see this during development')
 
</nowiki>
 
</div>
 
If multiple logs will be done it can be useful to temporarily save the console object and use that one.
 
 
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
<nowiki>
 
console = self._context.console
 
 
 
console.log('Everyone can always see this')
 
</nowiki>
 
 
</div>
 
</div>
 
==Accessing the canvas object==
 
The canvas object which is used to create graphical objects is part of the context. To create a simple red circle placed 5 units into the canvas, sized 2 canvas units, one can use
 
 
<code>
 
self._context.canvas.new_circle(2, 'red', x=5, y=5)
 
</code>
 
  
 
To avoid writing “self._context.” every time one can temporarily store the canvas as a separate variable.
 
To avoid writing “self._context.” every time one can temporarily store the canvas as a separate variable.
Line 50: Line 16:
 
  <nowiki>
 
  <nowiki>
 
canvas = self._context.canvas
 
canvas = self._context.canvas
 
+
canvas.new_circle(2, 'red', x=5, y=5)</nowiki>
canvas.new_circle(2, 'red', x=5, y=5)
 
</nowiki>
 
 
</div>
 
</div>
  
===Creating graphical objects===
+
==Creating graphical objects==
There are 5 main types of elements that can be used in the canvas. Rectangle, circle, polygon, text, bitmap, and spritesheet. All of the elements are created by accessing their respective method in the canvas object.
+
There are 6 main types of elements that can be used in the canvas; rectangle, circle, polygon, text, bitmap and spritesheet. All of the elements are created by accessing their respective method in the canvas object.
 
All color properties are set using CSS compatible names, such as "#FF6347" or "red".
 
All color properties are set using CSS compatible names, such as "#FF6347" or "red".
 
All rotation values are set using degrees. Rotating halfway through a full rotation is 180 degrees.
 
All rotation values are set using degrees. Rotating halfway through a full rotation is 180 degrees.
Line 63: Line 27:
 
If the edge argument is available and used the object will be an outline of the object with no fill color.
 
If the edge argument is available and used the object will be an outline of the object with no fill color.
 
===Rectangle===
 
===Rectangle===
<code>new_rectangle(width, height, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)</code>
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
 +
<nowiki>
 +
new_rectangle(width, height, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)</nowiki>
 +
</div>
 
Creates and returns a graphical object of the type rectangle.
 
Creates and returns a graphical object of the type rectangle.
 
Rectangles are defined by their width, height, and color. Their origo is in the middle of the shape and needs to be offset by half the width and height if you want to artificially move it to one of the corners.
 
Rectangles are defined by their width, height, and color. Their origo is in the middle of the shape and needs to be offset by half the width and height if you want to artificially move it to one of the corners.
 
Example:
 
Example:
<code>canvas.new_rectangle(4, 2, '#FF6347', x=3, y=3)
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
canvas.new_rectangle(2, 2, 'red', x=2, y=2, rotation=60)
+
<nowiki>
canvas.new_rectangle(2, 2, 'green', x=3, y=3, rotation=-20, edge=0.2, opacity=0.3)
+
a) canvas.new_rectangle(4, 2, '#FF6347', x=3, y=3)
</code>
+
b) canvas.new_rectangle(2, 2, 'red', x=2, y=2, rotation=60)
 +
c) canvas.new_rectangle(2, 2, 'green', x=3, y=3, rotation=-20, edge=0.2, opacity=0.3)</nowiki>
 +
</div>
 +
[[File:three_rectangles.png|Three rectangles]]
  
 
===Circle===
 
===Circle===
<code>new_circle(radius, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)</code>
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
 +
<nowiki>
 +
new_circle(radius, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)</nowiki>
 +
</div>
  
 
Creates and returns a graphical object of the type circle.
 
Creates and returns a graphical object of the type circle.
 
Circles are defined by their radius and their color. Their origo is in the middle of the shape and needs to be offset by the radius if you want to artificially move it to one of the corners.
 
Circles are defined by their radius and their color. Their origo is in the middle of the shape and needs to be offset by the radius if you want to artificially move it to one of the corners.
 
Example:
 
Example:
<code>canvas.new_circle(1, 'red')
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
canvas.new_circle(1, 'blue', x=2, y=1)
+
<nowiki>
canvas.new_circle(0.8, 'green', x=1, y=1, edge=0.1)
+
a) canvas.new_circle(1, 'red')
</code>
+
b) canvas.new_circle(1, 'blue', x=2, y=1)
 +
c) canvas.new_circle(0.8, 'green', x=1, y=1, edge=0.1)</nowiki>
 +
</div>
 +
[[File:three_circles.png|Three circles]]
  
 
===Polygon===
 
===Polygon===
<code>new_polygon(points, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)</code>
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
 +
<nowiki>
 +
new_polygon(points, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)</nowiki>
 +
</div>
 
Creates and returns a graphical object of the type polygon.
 
Creates and returns a graphical object of the type polygon.
 
Polygons are abstract shapes defined by a list of coordinates and a color. The list contains pairs of x and y coordinates but without any special separation, e.g. [x1, y1, x2, y2, x3, y3].
 
Polygons are abstract shapes defined by a list of coordinates and a color. The list contains pairs of x and y coordinates but without any special separation, e.g. [x1, y1, x2, y2, x3, y3].
Line 91: Line 70:
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
  <nowiki>
 
  <nowiki>
canvas.new_polygon([0, 0, 5, 0, 2, 1], 'red', x=3, y=1, z_index=1)
+
a) canvas.new_polygon([0, 0, 5, 0, 2, 1], 'red', x=3, y=1, z_index=1)
canvas.new_polygon([0, 0, 0, 2, 0.5, 2, 1, 1, 2, 2, 1, 0], 'blue', x=1, y=1, edge=0.3, z_index=3)
+
b) canvas.new_polygon([0, 0, 0, 2, 0.5, 2, 1, 1, 2, 2, 1, 0], 'blue', x=1, y=1, edge=0.3, z_index=3)
canvas.new_polygon([0, 0, -2, 2, 2, 2, 3, 0], 'green', x=4, y=3, opacity=0.5, rotation=170, z_index=2)
+
c) canvas.new_polygon([0, 0, -2, 2, 2, 2, 3, 0], 'green', x=4, y=3, opacity=0.5, rotation=170, z_index=2)</nowiki>
</nowiki>
+
</div>
 +
[[File:three_polygons.png|Three polygons]]
 +
 
 +
====Polygon Lines====
 +
To create a line, draw a polygon with just two points and edge set to the desired width of the line.
 +
 
 +
<div class="mw-collapsible-content" style="background: #F0F0F0">
 +
<nowiki>
 +
# Will make a filled triangle
 +
canvas.new_polygon(points=[1,1, 2,2, 1,2], color='black')
 +
 
 +
# Will make an empty triangle
 +
canvas.new_polygon(points=[1,3, 2,4, 1,4], color='black', edge=0.1)
 +
 
 +
# Will make a line with edge as thickness
 +
canvas.new_polygon(points=[2,1, 3,2], color='red', edge=0.1)
 +
 
 +
# Will make a curved line
 +
canvas.new_polygon(points=[3,1, 4,1, 4,2, 4,1], color='blue', edge=0.1)
 +
 
 +
# Will make a curved line offset by x/y
 +
canvas.new_polygon(points=[0,0, 1,1, 1,0, 2,1, 1,0, 1,1], color='green', edge=0.1, x=2, y=3)
 +
 
 +
# Make a tuple list, extend it with backtrack, convert to polygon points and create the polygon
 +
tuple_list=[(0,0), (0,1), (1,0), (0,-1), (-1,0), (-1,1), (0,2), (2,0), (1,-1)]
 +
tuple_list.extend(tuple_list[len(tuple_list)-2:0:-1])
 +
point_list = [num for tup in tuple_list for num in tup]
 +
canvas.new_polygon(points=point_list, color='magenta', edge=0.1, x=6, y=2)</nowiki>
 
</div>
 
</div>
 +
 +
[[File:Polygon_lines.png|Polygon lines]]
  
 
===Text===
 
===Text===
Line 101: Line 109:
 
  <nowiki>
 
  <nowiki>
 
new_text(text, size, color, x=0, y=0, text_align='left', scale_x=1,  
 
new_text(text, size, color, x=0, y=0, text_align='left', scale_x=1,  
  scale_y=1, opacity=1, rotation=0, z_index=0, pivot_x=0, pivot_y=0)
+
  scale_y=1, opacity=1, rotation=0, z_index=0, pivot_x=0, pivot_y=0)</nowiki>
</nowiki>
 
 
</div>
 
</div>
 
Creates and returns a graphical object of the type text.
 
Creates and returns a graphical object of the type text.
Line 110: Line 117:
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
  <nowiki>
 
  <nowiki>
canvas.new_text('default left alignment', 0.8, 'blue', x=7, y=1)
+
a) canvas.new_text('default left alignment', 0.8, 'blue', x=7, y=1)
canvas.new_text('alignment also moves origo', 0.8, 'black', x=7, y=2, text_align='center')
+
b) canvas.new_text('alignment also moves origo', 0.8, 'black', x=7, y=2, text_align='center')
canvas.new_text('right alignment', 0.8, 'red', x=7, y=3, text_align='right')
+
c) canvas.new_text('right alignment', 0.8, 'red', x=7, y=3, text_align='right')</nowiki>
</nowiki>
 
 
</div>
 
</div>
 +
[[File:three_texts.png|Three texts]]
  
 
===Bitmap===
 
===Bitmap===
<code>new_bitmap(width, height, image, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, color='white', pivot_x=0, pivot_y=0)</code>
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
 +
<nowiki>
 +
new_bitmap(width, height, image, x=0, y=0, scale_x=1, scale_y=1,  
 +
opacity=1, rotation=0, z_index=0, color='white', pivot_x=0, pivot_y=0)</nowiki>
 +
</div>
 
Creates and returns a graphical object of the type bitmap.
 
Creates and returns a graphical object of the type bitmap.
 
Width and height defines the size of the image, and will stretch the image if the original dimensions are not the same. The image argument should be the name of the image as it is written in the graphics tab.
 
Width and height defines the size of the image, and will stretch the image if the original dimensions are not the same. The image argument should be the name of the image as it is written in the graphics tab.
 
Example:
 
Example:
  
<code>
+
<div class="mw-collapsible-content" style="background: #F0F0F0">
canvas.new_bitmap(4, 4, 'striped_cat.png')
+
<nowiki>
canvas.new_bitmap(4, 4, 'striped_cat.png', x=4, y=1.5)
+
a) canvas.new_bitmap(4, 4, 'striped_cat.png')
canvas.new_bitmap(8, 4, 'striped_cat.png', x=4, y=2, rotation=20, color='red', opacity=0.3, z_index=-1, scale_x=-1)
+
b) canvas.new_bitmap(4, 4, 'striped_cat.png', x=4, y=1.5)
</code>
+
c) canvas.new_bitmap(8, 4, 'striped_cat.png', x=4, y=2, rotation=20, color='red', opacity=0.3, z_index=-1, scale_x=-1)</nowiki>
 +
</div>
 +
[[File:three_bitmaps.png|Three bitmaps]]
  
 
===Sprite sheet===
 
===Sprite sheet===
 +
The sprite sheets requires that the defined image is the name of a compatible sprite sheet. Check out the [[Create Sprite Sheet]] page to create your own sprite sheets compatible with the Future Skill platform.
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
<nowiki>
+
<nowiki>
 
new_spritesheet(width, height, image, animation='default',  
 
new_spritesheet(width, height, image, animation='default',  
 
  frame_index_start=None, frame_index_end=None, x=0, y=0, scale_x=1, scale_y=1,
 
  frame_index_start=None, frame_index_end=None, x=0, y=0, scale_x=1, scale_y=1,
Line 137: Line 151:
 
</div>
 
</div>
 
Creates and returns a graphical object of the type sprite sheet, intended to be used for animations.
 
Creates and returns a graphical object of the type sprite sheet, intended to be used for animations.
Width and height defines the size of the object to be drawn, if the dimensions are not the same in the sprite sheet then the image will be stretched. Each individual image in an animation should be the same size or they will stretch weirdly. The image argument should be the name of the sheet as it is written in the graphics tab.
+
Width and height defines the size of the object to be drawn, if the dimensions are not the same in the sprite sheet then the image will be stretched. Each individual image in an animation should be the same size or they will stretch weirdly.
Spritesheets need to be generated from individual images with a separate image atlas defining where in the sheet each individual image is (atlas needs to be compatible with pixi-js). A free tool to do this is https://github.com/krzysztof-o/spritesheet.js. In the freecode platform sprite sheets are mainly intended to be used for animations. When adding a sprite sheet and its atlas it is possible to automatically generate a default animation which will be defined by the last available number in the individual images names. It is also possible to personally define other animations in the same sheet and they can be utilized by setting the animation argument when creating the new_spritesheet in the code.
+
The image argument should be the name of the sheet as it is written in the graphics tab. The sprite sheet in the graphics tab should have an image atlas defined alongside of it, the atlas should contain information about where each individual image is placed in the sheet and what animations can be done with the sheet. If no specific animation has been selected in the sprite_sheet ceation then the implementation will assume that there is an animation named "default" that it will use. When adding a sprite sheet and its atlas it is possible to automatically generate the default animation which will play all the frames in the sheet. It is also possible to personally define other animations in the same sheet and they can be utilized by setting the animation argument when creating the new_spritesheet in the code.
Each animation is defined as a list of frame names, using the frame_index_start and frame_index_end arguments it is possible to only run part of an animation, or to reverse the animation.
+
Each animation is defined as a list of frame names, by using the frame_index_start and frame_index_end arguments in the implementation it is possible to only run part of an animation, or to reverse the animation.
 
Example:
 
Example:
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
<div class="mw-collapsible-content" style="background: #F0F0F0">
 
  <nowiki>
 
  <nowiki>
canvas.new_spritesheet(2, 2, 'weird_sheet.png', x=1, y=1)
+
a) canvas.new_spritesheet(2, 2, 'weird_sheet.png', x=1, y=1)
canvas.new_spritesheet(2, 2, 'weird_sheet.png', x=3, y=1, frame_index_start=2, frame_index_end=0, scale_x=-1)
+
b) canvas.new_spritesheet(2, 2, 'weird_sheet.png', x=3, y=1, frame_index_start=2, frame_index_end=0, scale_x=-1)
canvas.new_spritesheet(1.5, 2, 'weird_sheet.png', x=5, y=1, animation='just_display_guy')
+
c) canvas.new_spritesheet(1.5, 2, 'weird_sheet.png', x=5, y=1, animation='just_display_guy')</nowiki>
</nowiki>
 
 
</div>
 
</div>
 +
[[File:three_spritesheets.png|Three spritesheets]]
 +
 
The "weird_sheet.png" used in the example only contains 3 very different images so it will be easy to see the difference in a static context, normally you want to use variations of the same image in each sheet in order to get nice animations.
 
The "weird_sheet.png" used in the example only contains 3 very different images so it will be easy to see the difference in a static context, normally you want to use variations of the same image in each sheet in order to get nice animations.
  
===Manipulating graphical objects===
+
===Animating graphical objects===
Once a graphical object has been created there are various methods that can be used to manipulate them. All graphical objects have all methods, but they might not work for all types. Such as why rotate a circle, or why set a text on something that is not a text?
+
Once a graphical object has been created there are various methods that can be used to manipulate them over time to create an animation. All graphical objects have all methods listed below, but they might not work for all types. For example rotating a circle, or setting a text on something that is not a "Text" will yield no noticeable change.
The time argument is always a percentage ranging from 0 to 1, and represents when during a step the update will occur. For some operations it is necessary to first set the object to its original value and then to the new one. For a smooth movement you need to set the current position at time 0.0, and to the new position at the desired time. If you want to make an object suddenly change opacity at a specific time, then you need to set it to the current opacity just before making the change, or the change will be gradual from the start of the step time.
+
 
 +
The time argument is always a percentage ranging from 0.0 to 1.0 (where 1.0 is equal to 100% or the end of the step), and represents when during a step the update will occur. Most animations will render in a continuous manner, for example going from opacity 0 at 0% of a step to opacity 1 at 100% of a step will make the opacity change gradually over the step. If you want to make an object suddenly change opacity at a specific time, then you need to set it to the current opacity just before making the change, e.g. opacity 0 at 0.49 and opacity 1 at 0.5.
 +
 
 +
===Composite objects===
 +
The following example shows both how a parent object can be set when creating a graphical object and how the parent can be changed later using set_parent.
 +
<nowiki>
 +
    canvas = self._context.canvas
 +
    self.squares = [canvas.new_rectangle(1,1, 'white', x=2+i*2, y=2) for i in range(5)]
 +
    self.circle = canvas.new_circle(0.5, 'green', x=.5, y=.5, parent=self.squares[0])
 +
    self.circle.set_parent(self.squares[0])
 +
    text = canvas.new_text("wiiee", 0.4, 'lightgreen', x = 0, y=0, text_align='center')
 +
    text.set_parent(self.circle)</nowiki>
 +
 
 +
 
 +
Most operations in the section "Methods on graphical objects" below will then apply to both parent and child object.
 +
 
 +
=Example of an animation=
 +
 
 +
Our example animation uses a red rectangle standing on the ground (a black rectangle). It is played out over two steps; in the first step the red rectangle is static and in the second step it falls to the ground (with higher speed towards the end of the second step).
 +
 
 +
Note: To get an animation to work, go to the Graphics tab in Freecode Creator and check "Uses Canvas" and "Has Canvas Animation".
 +
 
 +
Freecode which implements this animation: https://futureskill.com/freecode-creator/602e752d1667cc6d98aee706 (can only be seen if logged in)
 +
 
 +
[[File:Falling_rectangle.gif]]
 +
 
 +
Image 1: The above animated gif shows our example animation as it looks in the Freecode canvas on our page.
 +
 
 +
[[File:Falling rectangle coordinates.png]]
  
 +
Image 2: Note that the y axis starts from the top (y=0) and has increasing values of y the further down in the canvas we are, whereas x=0 is to the very left. In this case the canvas is 20 units wide and 10 units high, so x can vary from 0 to 20 and y from 0 to 10.
 +
 +
[[File:Animation_of_falling_rectangle_explanation_figure.png]]
 +
 +
Image 3: In the above picture there is a timeline showing our example animation over two steps.
 +
 +
 +
'''Explanation''' (A, B , C etc. are shown in Image 3 above)
 +
 +
* A) We have created the rectangle and it is so far static and the bottom right corner is at x=6 and y=9 (note: in Image 2 above the x and y values can be seen, the grey dots represent integer values of x and y).
 +
 +
: The line in the Implementation of the Freecode is:
 +
: <code>self.falling_rect = canvas.new_rectangle(2, 8, 'red', x=6, y=9, pivot_x=1, pivot_y=4)</code>
 +
 +
: Normally a rectangle is positioned using the center point (so we would use x=5, y=5). However, in this case we use (pivot_x=1, pivot_y=4) which offsets the anchor point (pivot) by +1 on the x axis and +4 on the y axis. This means (since the rectangle is 2 wide and 8 high) that we now position the rectangle using the bottom right corner, which is placed at (x=6, y=9).
 +
 +
* B) The first step we don't carry out any animation, so the red rectangle is still static.
 +
 +
* C) In the second step (1) we carry out a 45 degree rotation at time point (sub-step time) 0.7 and since the canvas will 'tween' (https://en.wikipedia.org/wiki/Inbetweening) the rectangle from time 0.0 to 0.7 it will have a 22.5 degree rotation at time 0.35.
 +
 +
* D) At time 0.7 the rectangle will have a 45 degree rotation, around the anchor point at (x=6, y=9).
 +
 +
: The line in the code is:
 +
: <code>self.falling_rect.rotate(45, 0.7)</code>
 +
 +
* E) Again, a tween gives the rotation 67.5 degrees at time 0.85 as a result of the two rotation commands.
 +
 +
* F) A final rotation at time 1.0 completes the full rotation of 90 degrees, and the red rectangle lies flat on the ground.
 +
 +
: The line in the code is:
 +
: <code>self.falling_rect.rotate(45, 1.0)</code>
 +
 +
=Methods on graphical objects=
  
 
<code>get_name()</code>
 
<code>get_name()</code>
Line 220: Line 296:
 
<code>rotate(rotation, time=None)</code>
 
<code>rotate(rotation, time=None)</code>
 
Rotates the graphical object by the specified amount, relative to the objects previous rotation (in degrees).
 
Rotates the graphical object by the specified amount, relative to the objects previous rotation (in degrees).
 +
 +
<code>set_parent(<graphical_object>)</code>
 +
Set the parent graphical object of this graphical objects. Most operations in this list will then apply to both parent and child object.
  
 
<code>set_pivot(x=0, y=0, time=None)</code>
 
<code>set_pivot(x=0, y=0, time=None)</code>
Line 238: Line 317:
 
===Manually add animation step===
 
===Manually add animation step===
 
It is possible to manually add animation steps without going through the natural step cycle. This can be useful if you want to perform an extra step such as drawing a final score or perform some elaborate animation.
 
It is possible to manually add animation steps without going through the natural step cycle. This can be useful if you want to perform an extra step such as drawing a final score or perform some elaborate animation.
<code>canvas.finish_step()</code>
+
<code>canvas.finish_step()</code> (or, an alias is <code>canvas.split_step()</code>)
  
 
All graphical operations performed after finish_step will occur in a new step.
 
All graphical operations performed after finish_step will occur in a new step.
 +
 +
The example Freecode from above uses <code>canvas.finish_step()</code> on row 93: https://app.futureskill.com/freecode-test-editor/602e752d1667cc6d98aee706 (can only be seen if logged in).

Latest revision as of 10:31, 11 June 2024

Graphics utility functionality in Freecode creator

Enable graphics and animations

In the Freecode creator there is a page with settings for graphics, in this tab you have to enable "Uses canvas" in order to enable the canvas where graphics are displayed. In order to use animations you also have to check "has Canvas animation". Without the second option the canvas will only display 1 static update when running the code.

Accessing the canvas object

The canvas object which is used to create graphical objects is part of the context. To create a simple red circle placed 5 units into the canvas, sized 2 canvas units, one can use

self._context.canvas.new_circle(2, 'red', x=5, y=5)

To avoid writing “self._context.” every time one can temporarily store the canvas as a separate variable.

canvas = self._context.canvas
canvas.new_circle(2, 'red', x=5, y=5)

Creating graphical objects

There are 6 main types of elements that can be used in the canvas; rectangle, circle, polygon, text, bitmap and spritesheet. All of the elements are created by accessing their respective method in the canvas object. All color properties are set using CSS compatible names, such as "#FF6347" or "red". All rotation values are set using degrees. Rotating halfway through a full rotation is 180 degrees. The z_index is used to determine which object should overlap which. A higher z_index means the object will be drawn on top of objects with a lower value if they overlap. All positional arguments are either relative to the upper left corner of the canvas, or relative to origo of its own position. If x and y are not set they will both be 0 by default. If the edge argument is available and used the object will be an outline of the object with no fill color.

Rectangle

new_rectangle(width, height, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)

Creates and returns a graphical object of the type rectangle. Rectangles are defined by their width, height, and color. Their origo is in the middle of the shape and needs to be offset by half the width and height if you want to artificially move it to one of the corners. Example:

a) canvas.new_rectangle(4, 2, '#FF6347', x=3, y=3)
b) canvas.new_rectangle(2, 2, 'red', x=2, y=2, rotation=60)
c) canvas.new_rectangle(2, 2, 'green', x=3, y=3, rotation=-20, edge=0.2, opacity=0.3)

Three rectangles

Circle

new_circle(radius, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)

Creates and returns a graphical object of the type circle. Circles are defined by their radius and their color. Their origo is in the middle of the shape and needs to be offset by the radius if you want to artificially move it to one of the corners. Example:

a) canvas.new_circle(1, 'red')
b) canvas.new_circle(1, 'blue', x=2, y=1)
c) canvas.new_circle(0.8, 'green', x=1, y=1, edge=0.1)

Three circles

Polygon

new_polygon(points, color, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, edge=None, pivot_x=0, pivot_y=0)

Creates and returns a graphical object of the type polygon. Polygons are abstract shapes defined by a list of coordinates and a color. The list contains pairs of x and y coordinates but without any special separation, e.g. [x1, y1, x2, y2, x3, y3]. Example:

a) canvas.new_polygon([0, 0, 5, 0, 2, 1], 'red', x=3, y=1, z_index=1)
b) canvas.new_polygon([0, 0, 0, 2, 0.5, 2, 1, 1, 2, 2, 1, 0], 'blue', x=1, y=1, edge=0.3, z_index=3)
c) canvas.new_polygon([0, 0, -2, 2, 2, 2, 3, 0], 'green', x=4, y=3, opacity=0.5, rotation=170, z_index=2)

Three polygons

Polygon Lines

To create a line, draw a polygon with just two points and edge set to the desired width of the line.

# Will make a filled triangle
canvas.new_polygon(points=[1,1, 2,2, 1,2], color='black')

# Will make an empty triangle
canvas.new_polygon(points=[1,3, 2,4, 1,4], color='black', edge=0.1)

# Will make a line with edge as thickness
canvas.new_polygon(points=[2,1, 3,2], color='red', edge=0.1)

# Will make a curved line
canvas.new_polygon(points=[3,1, 4,1, 4,2, 4,1], color='blue', edge=0.1)

# Will make a curved line offset by x/y
canvas.new_polygon(points=[0,0, 1,1, 1,0, 2,1, 1,0, 1,1], color='green', edge=0.1, x=2, y=3)

# Make a tuple list, extend it with backtrack, convert to polygon points and create the polygon
tuple_list=[(0,0), (0,1), (1,0), (0,-1), (-1,0), (-1,1), (0,2), (2,0), (1,-1)]
tuple_list.extend(tuple_list[len(tuple_list)-2:0:-1])
point_list = [num for tup in tuple_list for num in tup]
canvas.new_polygon(points=point_list, color='magenta', edge=0.1, x=6, y=2)

Polygon lines

Text

new_text(text, size, color, x=0, y=0, text_align='left', scale_x=1, 
 scale_y=1, opacity=1, rotation=0, z_index=0, pivot_x=0, pivot_y=0)

Creates and returns a graphical object of the type text. Text is defined by the text, size, and color. Origo is to the left of the text by default but can be changed with the text_align argument. By default text_align is "left", other possible values are "right", and "center". Example:

a) canvas.new_text('default left alignment', 0.8, 'blue', x=7, y=1)
b) canvas.new_text('alignment also moves origo', 0.8, 'black', x=7, y=2, text_align='center')
c) canvas.new_text('right alignment', 0.8, 'red', x=7, y=3, text_align='right')

Three texts

Bitmap

new_bitmap(width, height, image, x=0, y=0, scale_x=1, scale_y=1, 
 opacity=1, rotation=0, z_index=0, color='white', pivot_x=0, pivot_y=0)

Creates and returns a graphical object of the type bitmap. Width and height defines the size of the image, and will stretch the image if the original dimensions are not the same. The image argument should be the name of the image as it is written in the graphics tab. Example:

a) canvas.new_bitmap(4, 4, 'striped_cat.png')
b) canvas.new_bitmap(4, 4, 'striped_cat.png', x=4, y=1.5)
c) canvas.new_bitmap(8, 4, 'striped_cat.png', x=4, y=2, rotation=20, color='red', opacity=0.3, z_index=-1, scale_x=-1)

Three bitmaps

Sprite sheet

The sprite sheets requires that the defined image is the name of a compatible sprite sheet. Check out the Create Sprite Sheet page to create your own sprite sheets compatible with the Future Skill platform.

new_spritesheet(width, height, image, animation='default', frame_index_start=None, frame_index_end=None, x=0, y=0, scale_x=1, scale_y=1, opacity=1, rotation=0, z_index=0, color='white', pivot_x=0, pivot_y=0)

Creates and returns a graphical object of the type sprite sheet, intended to be used for animations. Width and height defines the size of the object to be drawn, if the dimensions are not the same in the sprite sheet then the image will be stretched. Each individual image in an animation should be the same size or they will stretch weirdly. The image argument should be the name of the sheet as it is written in the graphics tab. The sprite sheet in the graphics tab should have an image atlas defined alongside of it, the atlas should contain information about where each individual image is placed in the sheet and what animations can be done with the sheet. If no specific animation has been selected in the sprite_sheet ceation then the implementation will assume that there is an animation named "default" that it will use. When adding a sprite sheet and its atlas it is possible to automatically generate the default animation which will play all the frames in the sheet. It is also possible to personally define other animations in the same sheet and they can be utilized by setting the animation argument when creating the new_spritesheet in the code. Each animation is defined as a list of frame names, by using the frame_index_start and frame_index_end arguments in the implementation it is possible to only run part of an animation, or to reverse the animation. Example:

a) canvas.new_spritesheet(2, 2, 'weird_sheet.png', x=1, y=1)
b) canvas.new_spritesheet(2, 2, 'weird_sheet.png', x=3, y=1, frame_index_start=2, frame_index_end=0, scale_x=-1)
c) canvas.new_spritesheet(1.5, 2, 'weird_sheet.png', x=5, y=1, animation='just_display_guy')

Three spritesheets

The "weird_sheet.png" used in the example only contains 3 very different images so it will be easy to see the difference in a static context, normally you want to use variations of the same image in each sheet in order to get nice animations.

Animating graphical objects

Once a graphical object has been created there are various methods that can be used to manipulate them over time to create an animation. All graphical objects have all methods listed below, but they might not work for all types. For example rotating a circle, or setting a text on something that is not a "Text" will yield no noticeable change.

The time argument is always a percentage ranging from 0.0 to 1.0 (where 1.0 is equal to 100% or the end of the step), and represents when during a step the update will occur. Most animations will render in a continuous manner, for example going from opacity 0 at 0% of a step to opacity 1 at 100% of a step will make the opacity change gradually over the step. If you want to make an object suddenly change opacity at a specific time, then you need to set it to the current opacity just before making the change, e.g. opacity 0 at 0.49 and opacity 1 at 0.5.

Composite objects

The following example shows both how a parent object can be set when creating a graphical object and how the parent can be changed later using set_parent.

    canvas = self._context.canvas
    self.squares = [canvas.new_rectangle(1,1, 'white', x=2+i*2, y=2) for i in range(5)]
    self.circle = canvas.new_circle(0.5, 'green', x=.5, y=.5, parent=self.squares[0])
    self.circle.set_parent(self.squares[0])
    text = canvas.new_text("wiiee", 0.4, 'lightgreen', x = 0, y=0, text_align='center')
    text.set_parent(self.circle)


Most operations in the section "Methods on graphical objects" below will then apply to both parent and child object.

Example of an animation

Our example animation uses a red rectangle standing on the ground (a black rectangle). It is played out over two steps; in the first step the red rectangle is static and in the second step it falls to the ground (with higher speed towards the end of the second step).

Note: To get an animation to work, go to the Graphics tab in Freecode Creator and check "Uses Canvas" and "Has Canvas Animation".

Freecode which implements this animation: https://futureskill.com/freecode-creator/602e752d1667cc6d98aee706 (can only be seen if logged in)

Falling rectangle.gif

Image 1: The above animated gif shows our example animation as it looks in the Freecode canvas on our page.

Falling rectangle coordinates.png

Image 2: Note that the y axis starts from the top (y=0) and has increasing values of y the further down in the canvas we are, whereas x=0 is to the very left. In this case the canvas is 20 units wide and 10 units high, so x can vary from 0 to 20 and y from 0 to 10.

Animation of falling rectangle explanation figure.png

Image 3: In the above picture there is a timeline showing our example animation over two steps.


Explanation (A, B , C etc. are shown in Image 3 above)

  • A) We have created the rectangle and it is so far static and the bottom right corner is at x=6 and y=9 (note: in Image 2 above the x and y values can be seen, the grey dots represent integer values of x and y).
The line in the Implementation of the Freecode is:
self.falling_rect = canvas.new_rectangle(2, 8, 'red', x=6, y=9, pivot_x=1, pivot_y=4)
Normally a rectangle is positioned using the center point (so we would use x=5, y=5). However, in this case we use (pivot_x=1, pivot_y=4) which offsets the anchor point (pivot) by +1 on the x axis and +4 on the y axis. This means (since the rectangle is 2 wide and 8 high) that we now position the rectangle using the bottom right corner, which is placed at (x=6, y=9).
  • B) The first step we don't carry out any animation, so the red rectangle is still static.
  • C) In the second step (1) we carry out a 45 degree rotation at time point (sub-step time) 0.7 and since the canvas will 'tween' (https://en.wikipedia.org/wiki/Inbetweening) the rectangle from time 0.0 to 0.7 it will have a 22.5 degree rotation at time 0.35.
  • D) At time 0.7 the rectangle will have a 45 degree rotation, around the anchor point at (x=6, y=9).
The line in the code is:
self.falling_rect.rotate(45, 0.7)
  • E) Again, a tween gives the rotation 67.5 degrees at time 0.85 as a result of the two rotation commands.
  • F) A final rotation at time 1.0 completes the full rotation of 90 degrees, and the red rectangle lies flat on the ground.
The line in the code is:
self.falling_rect.rotate(45, 1.0)

Methods on graphical objects

get_name() Returns the name of the object. The name will be generated as a unique identifier.

set_x(x, time=None) Sets the x coordinate of the graphical object (relative to the upper left corner).

get_x() Returns the x coordinate of the graphical object (relative to the upper left corner).

set_y(y, time=None) Sets the y coordinate of the graphical object (relative to the upper left corner).

get_y() Returns the y coordinate of the graphical object (relative to the upper left corner).

set_position(x, y, time=None) Sets the x and y coordinate of the graphical object (relative to the upper left corner).

get_position() Returns the x and y coordinate of the graphical object (relative to the upper left corner).

move(x=0, y=0, time=None) Changes the x and y coordinate of the graphical object by the specified amount, relative to the objects previous coordinates. For a smooth movement animation you first need to use move(0, 0, 0.0) followed by move(1, 0, 1.0), or it will just set the new position.

set_z_index(z, time=None) Sets the z index of the graphical object (higher z index objects are on top of lower ones).

get_z_index() Returns the z index of the graphical object (higher z index objects are on top of lower ones).

set_scale_x(scale_x, time=None) Sets the x scale of the graphical object (1 being default and 2 being twice the width). Useful for changing a pictures x orientation without setting a negative width.

get_scale_x() Returns the x scale of the graphical object (1 being default and 2 being twice the width).

set_scale_y(scale_y, time=None) Sets the y scale of the graphical object (1 being default and 2 being twice the height). Useful for changing a pictures y orientation without setting a negative height.

get_scale_y() Returns the y scale of the graphical object (1 being default and 2 being twice the height).

set_scale(scale, time=None) Sets the scale of the graphical object, both x and y (1 being default and 2 being twice the width and height).

get_scale() Returns the x and y scale of the graphical object (1 being default and 2 being twice the width and height).

set_color(color, time=None) Sets the color of the graphical object (CSS compatible colors, e.g. #FF4D4D or red).

set_opacity(opacity, time=None) Sets the opacity of the graphical object (0 being completely transparent and 1 being opaque).

get_opacity() Returns the opacity of the graphical object (0 being transparent and 1 being opaque).

set_rotation(rotation, time=None) Sets the rotation of the graphical object (in degrees).

get_rotation() Returns the rotation of the graphical object (in degrees).

rotate(rotation, time=None) Rotates the graphical object by the specified amount, relative to the objects previous rotation (in degrees).

set_parent(<graphical_object>) Set the parent graphical object of this graphical objects. Most operations in this list will then apply to both parent and child object.

set_pivot(x=0, y=0, time=None) Set the point around which an object should rotate. (relative to the regular origo)

get_pivot() Return the point around which an object should rotate. (relative to the regular origo)

set_text(text, time=None) Sets the text of the graphical object (only available on text objects).

get_text() Returns the text of the graphical object (only available on text objects).

destroy() Destroys the graphical object and removes it from the canvas (irreversible). If you try to update an object after it has been destroyed your implementation might crash.

Manually add animation step

It is possible to manually add animation steps without going through the natural step cycle. This can be useful if you want to perform an extra step such as drawing a final score or perform some elaborate animation. canvas.finish_step() (or, an alias is canvas.split_step())

All graphical operations performed after finish_step will occur in a new step.

The example Freecode from above uses canvas.finish_step() on row 93: https://app.futureskill.com/freecode-test-editor/602e752d1667cc6d98aee706 (can only be seen if logged in).