Vesmir
Vesmir is an artificial universe simulation. It generates a galaxy and about thousand stars in it. Some stars have planets to form solar systems, where some planets has moons as their companions. All the stars and planet images are modeled in realtime based on their chemical composition, so the outlook of those objects are quite close how they would look in reality.
Vesmir demonstrates drawing operations, menues, some of the widgets how they interact with window, and how to bind own C functions to Ano script. Vesmir is not by any means a real space simulator, it is just an example, it should not be taken too serious.
User interface
Move the mouse over the galaxy and press <enter> on some star. View changes to that star, and if star has planets you can see some squares rounding around a cross. Squares indicates planets and cross is the star at the center of the solarsystem. Move mouse over one of the planets, press <enter> again and view changes to planet view. Backspace key navigates back to previous view. Galaxy and solar systems can be zoomed with mouse wheel (or buttons 4 and 5 to be exact), and they can be moved around by pressing mouse button 1 (that's usually the left mouse button) while moving the mouse.
In galaxy there is black holes (hard to find, but one sits exactly at the center of the galaxy), neutron stars, ordinary stars like the Sun, terrestrial planets with atmosphere like Mars, and gas giants like Saturn. Try to find another Earth! Stars also simulates nuclear fusion, so after considerably amount of time you may see a message saying some star has collapsed to neutron star or black hole. Roll the Time warp to southeast and wait.
Star and planet details can be viewed by pressing Print object button. Details are printed to standard output. Star and planet images can be saved to disk by pressing Save object button. Render object button opens star or planet image in separate window.
View reset button resets the view to its default position and sets zooming to zero. Time reset button resets the time warp setting, making clock to run at normal speed.
Compilation
Easiest way to get Vesmir running is to go to examples directory in package root, and run:
$ ./build.sh gui_vesmir
build.sh script compiles Ano script, widget definitions and menu tables to C, copies source files in place and pops up instructions what to do next. Follow them. Check also examples/README for more info.
Preview
;
; @ANO_SCRIPT_NAME gui_vesmir
; @ANO_SCRIPT_VERSION 0.0.6
; @ANO_SCRIPT_DESCRIPTION Artificial universe simulation
;
; List of files to unveil if unveil() support is in use:
;
; @ANO_UNVEIL_FILES "examples/datafiles/vesmir_appicon.tga = r", \
; "share/detroit/fonts/NotoSans-Italic.ttf = r", \
; "share/detroit/fonts/NotoSans-Regular.ttf = r", \
; "/tmp = rw"
;
; @ANO_FLAGS_VAR_NAME_SUBS [x]
; @ANO_FLAGS_VAR_WARN_UNUSED [ ]
;
; Copyright (c) 2016-2025, Jani Salonen <salojan@goto10.co>
; All rights reserved.
;
main {
; Global uninitialized variables
var [handle] hnd_win_main
var [handle] hnd_win_view
var [handle] hnd_win_preview
var [handle] hnd_fnt_title
var [handle] hnd_fnt_view
var [handle] hnd_fnt_selected
var [handle] hnd_fnt_selection
; Global variables
mov img_dir ("/tmp")
mov hlp_txt ("LMB + mouse to move view, wheel to zoom, enter to select, backspace to previous view")
mov hlp_clr (# 0xff, 0xff, 0xff, 0x00)
mov fnt ("share/detroit/fonts/NotoSans-Regular.ttf")
mov fnt_clr (# 0xff, 0xd7, 0x69, 0x00)
mov fnt_sz_big ([uint] 14)
mov fnt_sz_med ([uint] 10)
mov fnt_sz_sml ([uint] 8)
mov view (0)
mov xpos (0)
mov ypos (0)
mov xpre (0)
mov ypre (0)
mov zoom_min (0)
mov zoom_max (100)
mov zoom_slc (10)
mov zoom (zoom_min)
mov galaxy ([ulong] 0)
mov star ([ulong] 0)
mov planet ([ulong] 0)
mov moon ([ulong] 0)
mov mess ([uint] 1)
gui_enabled {
; Initialize windowing system...
window_init
; ...and open main window
;
window_open (\
title_name: "Vesmir", \
title_charset: NULL, \
parent_handle: NOPARENT, \
widget_stack_id: 1, \
widget_set: 0, \
window_refresh_divider: PASSIVE_REFRESH, \
position_x: POS_CENTERED, \
position_y: POS_CENTERED, \
size_width: 800, \
size_height: 600, \
cb_main_loop: NULL, \
cb_expose: NULL, \
cb_key_press: NULL, \
cb_key_release: NULL, \
cb_button_press: NULL, \
cb_button_release: NULL, \
cb_client_message: NULL, \
cb_save_yourself: NULL, \
cb_configure_notify: NULL, \
cb_destroy_notify: "cb_destroy_main", \
cb_motion_notify: NULL, \
cb_map_notify: NULL, \
cb_unmap_notify: NULL, \
cb_open_notify: "cb_open_main")
}
}
;
; Main window callbacks
;
_WINCB_OPEN_ callback cb_open_main (_hnd) {
mov hnd_win_main (_hnd)
; Create timewarp widget scale
widget_update_scale_bars (_hnd, \
"time_warp", NULL, NULL, 7, 0, 0, 100, 11, \
"white")
; Set window icon
_hnd.icon("examples/datafiles/vesmir_appicon.tga")
; Open drawing window
window_open (NULL, NULL, \
_hnd, \
[uint] 0, [uint] 0, \
ACTIVE_REFRESH, \
[int] 20, [int] 180, [uint] 760, [uint] 400, \
"cb_mainloop_draw", \
NULL, \
"cb_keypress_draw", \
"cb_keyrelease_draw", \
"cb_buttonpress_draw", \
"cb_buttonrelease_draw", \
NULL, \
NULL, \
NULL, \
NULL, \
"cb_motion_draw", \
NULL, \
NULL, \
"cb_open_draw")
; Open fonts
font_open (fnt, fnt_sz_big, [uint] 0)
cmp rc (INVALID)
je "cb_open_main_end"
mov hnd_fnt_title (rc)
font_open (fnt, fnt_sz_big, [uint] 0)
cmp rc (INVALID)
je "cb_open_main_end"
mov hnd_fnt_view (rc)
font_open (fnt, fnt_sz_med, [uint] 0)
cmp rc (INVALID)
je "cb_open_main_end"
mov hnd_fnt_selected (rc)
font_open (fnt, fnt_sz_med, [uint] 0)
cmp rc (INVALID)
je "cb_open_main_end"
mov hnd_fnt_selection (rc)
font_open (fnt, fnt_sz_sml, [uint] 0)
cmp rc (INVALID)
je "cb_open_main_end"
mov hnd_fnt_date (rc)
; Pre-render some static strings to be displayed later
font_render (hnd_fnt_selected, \
"UTF-8", "Selected:", [uint] 0, fnt_clr)
font_render (hnd_fnt_view, \
"UTF-8", "View:", [uint] 0, fnt_clr)
; Map main window
_hnd.map()
cb_open_main_end:
}
_WINCB_DESTROY_ callback cb_destroy_main (_hnd) {
_hnd.destroy()
hnd_fnt_title.close()
hnd_fnt_view.close()
hnd_fnt_selected.close()
hnd_fnt_selection.close()
hnd_fnt_date.close()
exit
}
;
; Drawing window callbacks
;
_WINCB_MAINLOOP_ callback cb_mainloop_draw (_hnd) {
draw_wipe (hnd_win_view)
cmp view (0)
je "cb_mainloop_draw_galaxy"
cmp view (1)
je "cb_mainloop_draw_star"
cmp view (2)
je "cb_mainloop_draw_planet"
end
cb_mainloop_draw_galaxy:
; Main loop for galaxy view
mov galaxy_x (xpos)
mov galaxy_y (ypos)
mov galaxy_z (zoom)
galaxy_live (hnd_win_view, galaxy, \
galaxy_x, galaxy_y, galaxy_z)
galaxy_get_name (galaxy)
mov _name (rc)
call "cb_mainloop_draw_texts" (_name)
jmp "cb_mainloop_draw_done"
cb_mainloop_draw_star:
; Main loop for star view
mov star_x (xpos)
mov star_y (ypos)
mov star_z (zoom)
star_live (hnd_win_view, galaxy, star, \
star_x, star_y, star_z)
star_get_name (galaxy, star)
mov _name (rc)
call "cb_mainloop_draw_texts" (_name)
jmp "cb_mainloop_draw_done"
cb_mainloop_draw_planet:
; Main loop for planet view
mov planet_x (xpos)
mov planet_y (ypos)
mov planet_z (zoom)
planet_live (hnd_win_view, galaxy, star, planet, \
planet_x, planet_y, planet_z)
planet_get_name (galaxy, star, planet)
mov _name (rc)
call "cb_mainloop_draw_texts" (_name)
; jmp "cb_mainloop_draw_done"
cb_mainloop_draw_done:
; Zoom view slowly back to its base position
cmp zoom (zoom_min)
jbe "cb_mainloop_draw_end"
sub zoom (0.0005)
cb_mainloop_draw_end:
}
function cb_mainloop_draw_texts (_object) {
universe_chk_message
cmp mess (rc)
je "cb_mainloop_draw_texts_ok"
mov mess (rc)
universe_get_message
mov _mess (rc)
widget_update_label (hnd_win_main, "help", \
_mess, NULL, fnt, 0, 0, 0, hlp_clr)
widget_refresh (hnd_win_main)
cb_mainloop_draw_texts_ok:
universe_get_date
mov _date (rc)
font_render (hnd_fnt_date, \
"UTF-8", _date, [uint] 0, fnt_clr)
font_render (hnd_fnt_title, \
"UTF-8", _object, [uint] 0, fnt_clr)
font_get_width (hnd_fnt_date)
mov _w (rc)
font_get_height (hnd_fnt_date)
mov _h (rc)
draw_text (hnd_win_view, \
hnd_fnt_title, [int] 80, [int] 80)
draw_text (hnd_win_view, \
hnd_fnt_view, [int] 20, [int] 80)
draw_text (hnd_win_view, \
hnd_fnt_selected, [int] 20, [int] 120)
draw_text (hnd_win_view, \
hnd_fnt_selection, [int] 80, [int] 120)
draw_text (hnd_win_view, \
hnd_fnt_date, [int] 760 - _w, [int] 400 - _h)
}
_WINCB_KEY_ callback cb_keypress_draw (_hnd, x, y, _x_root, _y_root, _state, _keycode, _key) {
cmp _keycode (6)
je "cb_keypress_draw_enter"
cmp _keycode (2)
je "cb_keypress_draw_backspace"
end
cb_keypress_draw_enter:
cmp view (0)
je "cb_keypress_draw_enter_galaxy"
cmp view (1)
je "cb_keypress_draw_enter_star"
end
cb_keypress_draw_enter_galaxy:
; Switch from galaxy to star view
call "cb_keypress_draw_enter_switch" (1)
end
cb_keypress_draw_enter_star:
; Switch from star to planet view
call "cb_keypress_draw_enter_switch" (2)
end
cb_keypress_draw_backspace:
cmp view (1)
je "cb_keypress_draw_backspace_star"
cmp view (2)
je "cb_keypress_draw_backspace_planet"
end
cb_keypress_draw_backspace_star:
; Switch from star to galaxy view
call "cb_keypress_draw_backspace_switch" \
(galaxy_x, galaxy_y, galaxy_z, 0)
end
cb_keypress_draw_backspace_planet:
; Switch from planet to star view
call "cb_keypress_draw_backspace_switch" \
(star_x, star_y, star_z, 1)
}
function cb_keypress_draw_enter_switch (_switch) {
mov xpos (0)
mov ypos (0)
mov xpre (0)
mov ypre (0)
mov zoom (zoom_min)
mov view (_switch)
widget_update_label (hnd_win_main, "help", \
hlp_txt, NULL, fnt, 0, 0, 0, hlp_clr)
widget_refresh (hnd_win_main)
}
function cb_keypress_draw_backspace_switch (_switch_x, _switch_y, _switch_z, _switch) {
mov xpos (_switch_x)
mov ypos (_switch_y)
mov zoom (_switch_z)
mov view (_switch)
widget_update_label (hnd_win_main, "help", \
hlp_txt, NULL, fnt, 0, 0, 0, hlp_clr)
widget_refresh (hnd_win_main)
}
_WINCB_KEY_ callback cb_keyrelease_draw (_hnd, x, y, _x_root, _y_root, _state, _keycode, _key) {
}
_WINCB_BUTTON_ callback cb_buttonpress_draw (_hnd, x, y, _x_root, _y_root, _state, _button) {
cmp _button (1)
je "cb_buttonpress_draw_zoom_in"
cmp _button (5)
je "cb_buttonpress_draw_zoom_in"
cmp _button (3)
je "cb_buttonpress_draw_zoom_out"
cmp _button (4)
je "cb_buttonpress_draw_zoom_out"
; Clear previous pointer position for motion event
mov xpre (0)
mov ypre (0)
end
cb_buttonpress_draw_zoom_in:
; Zoom view inwards
cmp zoom (zoom_max)
jae "cb_buttonpress_draw_zoom_in_end"
mov _i (zoom_max)
sub _i (zoom)
div _i (zoom_slc)
add zoom (_i)
cb_buttonpress_draw_zoom_in_end:
end
cb_buttonpress_draw_zoom_out:
; Zoom view outwards
cmp zoom (zoom_min)
jbe "cb_buttonpress_draw_zoom_out_end"
mov _o (zoom_max)
sub _o (zoom)
div _o (zoom_slc)
sub zoom (_o)
cb_buttonpress_draw_zoom_out_end:
}
_WINCB_BUTTON_ callback cb_buttonrelease_draw (_hnd, x, y, _x_root, _y_root, _state, _button) {
}
_WINCB_MOTION_ callback cb_motion_draw (_hnd, x, y, _x_root, _y_root, _state, _detail) {
; 256 = Button1Mask, 1 << 8
;
; There may be other bits in _state, for example numlock, and if so, 256 is not true,
; so view wont move if numlock is on.
;
cmp _state (256)
jne "cb_motion_draw_view"
; Button 1 pressed with mouse motion, move view
cmp xpre (0)
jne "cb_motion_draw_a"
mov xpre (x)
cb_motion_draw_a:
cmp ypre (0)
jne "cb_motion_draw_b"
mov ypre (y)
cb_motion_draw_b:
mov _x (x)
mov _y (y)
sub _x (xpre)
sub _y (ypre)
mul _x (1.5)
mul _y (1.5)
add xpos (_x)
add ypos (_y)
mov xpre (x)
mov ypre (y)
end
cb_motion_draw_view:
; Select object closest to mouse pointer
cmp view (0)
je "cb_motion_draw_view_galaxy"
cmp view (1)
je "cb_motion_draw_view_star"
cmp view (2)
je "cb_motion_draw_view_planet"
end
cb_motion_draw_view_galaxy:
; Get the closest star
galaxy_live_closest (galaxy, x, y)
cmp rc (0)
je "cb_motion_draw_view_galaxy_end"
mov star (rc)
dec star
; Get the closest star name and render it
star_get_name (galaxy, star)
mov _name (rc)
font_render (hnd_fnt_selection, \
"UTF-8", _name, [uint] 0, fnt_clr)
cb_motion_draw_view_galaxy_end:
end
cb_motion_draw_view_star:
; Get the closest planet
star_live_closest (galaxy, star, x, y)
cmp rc (0)
je "cb_motion_draw_view_star_end"
mov planet (rc)
dec planet
; Get the closest planet name and render it
planet_get_name (galaxy, star, planet)
mov _name (rc)
font_render (hnd_fnt_selection, \
"UTF-8", _name, [uint] 0, fnt_clr)
cb_motion_draw_view_star_end:
end
cb_motion_draw_view_planet:
; Get the closest moon
planet_live_closest (galaxy, star, planet, x, y)
cmp rc (0)
je "cb_motion_draw_view_planet_end"
mov moon (rc)
dec moon
; Get the closest moon name and render it
satellite_get_name (galaxy, star, planet, moon)
mov _name (rc)
font_render (hnd_fnt_selection, \
"UTF-8", _name, [uint] 0, fnt_clr)
cb_motion_draw_view_planet_end:
}
_WINCB_OPEN_ callback cb_open_draw (_hnd) {
mov hnd_win_view (_hnd)
; Map drawing window
_hnd.map()
}
;
; Preview window callbacks
;
_WINCB_OPEN_ callback cb_open_preview (_hnd) {
mov hnd_win_preview (_hnd)
cmp view (1)
jne "cb_open_preview_planet"
star_render (hnd_win_preview, \
galaxy, star)
jmp "cb_open_preview_end"
cb_open_preview_planet:
cmp view (2)
jne "cb_open_preview_end"
planet_render (hnd_win_preview, \
galaxy, star, planet)
; jmp "cb_open_preview_end"
cb_open_preview_end:
; Map preview window
_hnd.map()
}
_WINCB_DESTROY_ callback cb_destroy_preview (_hnd) {
mov hnd_win_preview (INVALID)
_hnd.destroy()
}
;
; Widget callbacks
;
_WIDGETCB_PUSH_ callback cb_push_renderobject (_widget_name, _widget_id, _min, _max, _steps, _step) {
cmp view (0)
je "cb_push_renderobject_galaxy"
cmp view (1)
je "cb_push_renderobject_star"
cmp view (2)
je "cb_push_renderobject_planet"
end
cb_push_renderobject_galaxy:
universe_set_message \
("Rendering galaxy is not supported")
end
cb_push_renderobject_star:
; Open preview window for star
call "cb_push_renderobject_open" \
("Vesmir star preview")
end
cb_push_renderobject_planet:
; Open preview window for planet
call "cb_push_renderobject_open" \
("Vesmir planet preview")
}
function cb_push_renderobject_open (_title) {
cmp hnd_win_preview (INVALID)
je "cb_push_renderobject_ok"
window_close (hnd_win_preview)
cb_push_renderobject_ok:
window_open (_title, NULL, \
NOPARENT, \
[uint] 2, [uint] 0, \
PASSIVE_REFRESH, \
POS_CENTERED, POS_CENTERED, [uint] 256, [uint] 256, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
"cb_destroy_preview", \
NULL, \
NULL, \
NULL, \
"cb_open_preview")
}
_WIDGETCB_PUSH_ callback cb_push_printobject (_widget_name, _widget_id, _min, _max, _steps, _step) {
cmp view (0)
je "cb_push_printobject_galaxy"
cmp view (1)
je "cb_push_printobject_star"
cmp view (2)
je "cb_push_printobject_planet"
end
cb_push_printobject_galaxy:
; Print galaxy details
galaxy_print (galaxy)
end
cb_push_printobject_star:
; Print star details
star_print (galaxy, star)
end
cb_push_printobject_planet:
; Print planet details
planet_print (galaxy, star, planet)
}
_WIDGETCB_PUSH_ callback cb_push_saveobject (_widget_name, _widget_id, _min, _max, _steps, _step) {
cmp view (0)
je "cb_push_saveobject_galaxy"
cmp view (1)
je "cb_push_saveobject_star"
cmp view (2)
je "cb_push_saveobject_planet"
end
cb_push_saveobject_galaxy:
universe_set_message ("Saving galaxy is not supported")
end
cb_push_saveobject_star:
; Save star image
star_store (galaxy, star, img_dir)
end
cb_push_saveobject_planet:
; Save planet image
planet_store (galaxy, star, planet, img_dir)
}
_WIDGETCB_PUSH_ callback cb_push_viewreset (_widget_name, _widget_id, _min, _max, _steps, _step) {
; Reset the view
mov xpos (0)
mov ypos (0)
mov zoom (zoom_min)
}
_WIDGETCB_PUSH_ callback cb_push_timereset (_widget_name, _widget_id, _min, _max, _steps, _step) {
; Reset the clock
clock_set_multiplier (1)
widget_step_set (hnd_win_main, "time_warp", [uint] 1)
}
_WIDGETCB_TURN_ callback cb_turn_timewarp (_widget_name, _widget_id, _min, _max, _steps, _step) {
; Speed up the universe
mov _mul (((_step * _step) * _step) * _step)
clock_set_multiplier (_mul)
}
;
; Menu item callbacks
;
_MENUCB_ITEM_ callback cb_file_exit (_item, _position, _tag, _flag) {
exit
}
_MENUCB_ITEM_ callback cb_object_render (_item, _position, _tag, _flag) {
print "Not implemented, use widgets instead.\n"
}
_MENUCB_ITEM_ callback cb_object_print (_item, _position, _tag, _flag) {
print "Not implemented, use widgets instead.\n"
}
_MENUCB_ITEM_ callback cb_object_save (_item, _position, _tag, _flag) {
print "Not implemented, use widgets instead.\n"
}
_MENUCB_ITEM_ callback cb_time_reset (_item, _position, _tag, _flag) {
print "Not implemented, use widgets instead.\n"
}
_MENUCB_ITEM_ callback cb_time_set (_item, _position, _tag, _flag) {
print "Not implemented, use widgets instead.\n"
}
;
; Copyright (c) 2016-2025, Jani Salonen <salojan@goto10.co>
; All rights reserved.
;
; @WIDGET_USE_BOUNDING_BOXES no
;
window "1" {
set "0" {
widget "background" {
name "background"
state enable
image "examples/datafiles/background.tga"
}
widget "label" {
name "help"
state enable
position x = 20, y = 580
label "LMB + mouse to move view, wheel to zoom, enter to select, backspace to previous view", \
charset = UTF-8, size = 8, \
color = rgb:#ffffff, \
justification = left
}
widget "pushbutton_2"
name "render_object"
state enable
type 3
position x = 40, y = 100
title "Render object", \
charset = UTF-8, size = 10, \
color = rgb:#ffffff, \
justification = center, \
position = above, x = 0, y = -10
sticky no
action push = cb_push_renderobject
}
widget "pushbutton_2" {
name "print_object"
state enable
type 3
position x = 160, y = 100
title "Print object", \
charset = UTF-8, size = 10, \
color = rgb:#ffffff, \
justification = center, \
position = above, x = 0, y = -10
sticky no
action push = cb_push_printobject
}
widget "pushbutton_2" {
name "save_object"
state enable
type 3
position x = 280, y = 100
title "Save object", \
charset = UTF-8, size = 10, \
color = rgb:#ffffff, \
justification = center, \
position = above, x = 0, y = -10
sticky no
action push = cb_push_saveobject
}
widget "pushbutton_2" {
name "view_reset"
state enable
type 1
position x = 440, y = 100
title "View reset", \
charset = UTF-8, size = 10, \
color = rgb:#ffffff, \
justification = center, \
position = above, x = 0, y = -10
sticky no
action push = cb_push_viewreset
}
widget "pushbutton_2" {
name "time_reset"
state enable
type 1
position x = 560, y = 100
title "Time reset", \
charset = UTF-8, size = 10, \
color = rgb:#ffffff, \
justification = center, \
position = above, x = 0, y = -10
sticky no
action push = cb_push_timereset
}
widget "turnswitch" {
name "time_warp"
state enable
type 5
position x = 690, y = 65
angle min = 25, max = 335
steps start = 1, total = 1000
title "Time warp", \
charset = UTF-8, size = 10, \
color = rgb:#ffffff, \
justification = center, \
position = below, x = 0, y = 32
trigger constant
action turn = cb_turn_timewarp
}
}
}
;
; Copyright (c) 2016-2025, Jani Salonen <salojan@goto10.co>
; All rights reserved.
;
;
; Menu is attached to window where WIDGET_STACK_ID is set to 1.
;
; container_type "container_id" {
;
window "1" {
;
; menu "menu_name" { optional_icon
; --------- -------------
;
menu "File" { examples/datafiles/vesmir_menu_1.tga
;
; type "title", id, key, callback, flags, optional_icon
; ---- ----- -- --- -------- ----- -------------
;
item "Exit", 1, none, cb_file_exit, none, none
}
menu "Object" {
item "Render", 10, none, cb_object_render, none, none
item "Print", 11, none, cb_object_print, none, none
item "Save", 12, none, cb_object_save, none, none
}
menu "Time" {
item "Reset", 20, none, cb_time_reset, none, none
delim
radio "Increase 10%", 30, none, cb_time_set, none, none
radio "Increase 20%", 30, none, cb_time_set, none, none
radio "Increase 30%", 30, none, cb_time_set, none, none
radio "Increase 40%", 30, none, cb_time_set, none, none
radio "Increase 50%", 30, none, cb_time_set, none, none
radio "Increase 60%", 30, none, cb_time_set, none, none
radio "Increase 70%", 30, none, cb_time_set, none, none
radio "Increase 80%", 30, none, cb_time_set, none, none
radio "Increase 90%", 30, none, cb_time_set, none, none
radio "Increase 100%", 30, none, cb_time_set, none, none
}
}
Screenshots
Click to enlarge.
Copyright © 2025, Jani Salonen <salojan at goto10 piste co>. Piste is finnish word and means dot. All rights reserved.





