Microsoft Small Basic

Program Listing: TKW252-6
'--
' Aquarium 0.3
' Copyright © 2012-2017 Nonki Takahashi. All rights reserved.
'
' History:
' 0.3 2017-04-23 Shapes version. (TKW252-6)
' 0.2 2014-03-14 Accurate centering version. (TKW252-0)
' 0.1 2012-06-26 Created. (TKW252)
'
GraphicsWindow.Title = "Aquarium 0.3"
Not = "False=True;True=False;"
WQ = Text.GetCharacter(34)
SB_Workaround()
Colors_Init()
gw = 598
gh = 428
GraphicsWindow.Width = gw
GraphicsWindow.Height = gh
width = 400
height = 300
depth = 230
gap = 10
hCaption = 16
x = Math.Floor((gw - width) / 2)
y = Math.Floor((gh - height - gap - hCaption) / 2)
' draw aqarium
GraphicsWindow.BackgroundColor = "#333333"
GraphicsWindow.PenColor = "White"
GraphicsWindow.DrawRectangle(x, y, width, height)
' draw caption
GraphicsWindow.FontName = "Tahoma"
GraphicsWindow.BrushColor = "Black"
caption = "Orchid Fish"
name = caption
wCaption = 68 + 2 * 7
GraphicsWindow.FillRectangle(x + (width - wCaption) / 2, y + height + gap, wCaption, hCaption)
GraphicsWindow.BrushColor = "White"
GraphicsWindow.DrawBoundText(x + (width - wCaption) / 2 + 7, y + height + gap, wCaption, caption)
AddBall()
AddBubbles()
AddFish()
AddWater()
StartTimer()

Sub AddFish
nFish = 3
hFish = 20 ' body height
wFish = hFish * 2 ' body width
colorPen = "DarkOrchid"
colorBrush = "Orchid"
param["pw"] = 2
For i = 1 To nFish
shape = ""
iMax = 0
Stack.PushValue("local", x)
Stack.PushValue("local", y)
shX = x + 10 + Math.GetRandomNumber(width - 10 - hFish * 3)
shY = y + height - hFish - Math.GetRandomNumber(depth - hFish)
opacity = 60
color = colorPen
Color_SetAlpha()
param["pc"] = color ' tail color
color = colorBrush
Color_SetAlpha()
param["bc"] = color ' tail color
param["width"] = hFish * 1.5
param["height"] = hFish * 0.5
param["func"] = "rect"
param["x"] = 0
param["y"] = hFish * 0.25
Shapes_AddFunc()
param["pc"] = colorPen ' fish color
param["bc"] = colorBrush ' fish color
param["width"] = wFish
param["height"] = hFish
param["func"] = "ell"
param["x"] = hFish
param["y"] = 0
Shapes_AddFunc()
param["pc"] = "White" ' eye color
param["bc"] = "Black" ' eye color
param["width"] = hFish * 0.5
param["height"] = hFish * 0.5
param["func"] = "ell"
param["x"] = hFish * 2.25
param["y"] = hFish * 0.25
Shapes_AddFunc()
y = Stack.PopValue("local")
x = Stack.PopValue("local")
Group_Add()
EndFor
EndSub

Sub MoveFish
Stack.PushValue("local", x)
Stack.PushValue("local", y)
xo = x
yo = y
distance = 20
For i = 1 To nFish
grp = group[i]
x = grp["x"] + distance * grp["dir"]
y = grp["y"]
Group_Move()
If x < xo + (distance + 10) Then
Timer.Pause()
Group_Flip()
Timer.Resume()
ElseIf xo + width - (distance + 10) < x + hFish * 3 Then
Timer.Pause()
Group_Flip()
Timer.Resume()
EndIf
EndFor
y = Stack.PopValue("local")
x = Stack.PopValue("local")
EndSub

Sub AddBubbles
nBubbles = 10
GraphicsWindow.PenColor = "White"
GraphicsWindow.BrushColor = "White"
For i = 1 To nBubbles
oBubbles[i] = Shapes.AddEllipse(5, 5)
xBubbles[i] = x + 60 + Math.GetRandomNumber(50)
yBubbles[i] = y + height - 25 - Math.GetRandomNumber(depth - 25)
EndFor
EndSub

Sub MoveBubbles
up = 40
For i = 1 To nBubbles
yBubbles[i] = yBubbles[i] - up
If yBubbles[i] < y + height - depth Then
xBubbles[i] = x + 60 + Math.GetRandomNumber(50)
yBubbles[i] = y + height - 25 - Math.GetRandomNumber(up)
EndIf
Shapes.Move(oBubbles[i], xBubbles[i], yBubbles[i])
EndFor
EndSub

Sub AddBall
Stack.PushValue("local", x)
Stack.PushValue("local", y)
GraphicsWindow.PenColor = "DarkGray"
GraphicsWindow.BrushColor = "DarkGray"
oAquarium = Shapes.AddEllipse(50, 50)
x = x + 60
y = y + height - 50
Shapes.Move(oAquarium, x, y)
Shapes.SetOpacity(oAquarium, 50)
y = Stack.PopValue("local")
x = Stack.PopValue("local")
EndSub

Sub AddWater
Stack.PushValue("local", y)
y = y + height - depth
GraphicsWindow.PenColor = "White"
GraphicsWindow.BrushColor = "Aquamarine"
oAquarium = Shapes.AddRectangle(width, depth)
Shapes.Move(oAquarium, x, y)
Shapes.SetOpacity(oAquarium, 10)
y = Stack.PopValue("local")
EndSub

Sub StartTimer
Timer.Interval = 200
Timer.Tick = OnTick
EndSub

Sub OnTick
MoveBubbles()
MoveFish()
EndSub

Sub Color_NameToColor
'--
' Color | Convert color name to color
' param color - color name
' return color -"#rrggbb"
If Text.StartsWith(color, "#") Then
color = Text.ConvertToUpperCase(color)
Else
color = Text.ConvertToLowerCase(color)
color = colors[color]
EndIf
EndSub

Sub Color_SetAlpha
'--
' Color | Set alpha
' param color - color to set alpha blending
' param opacity - opacity
' return color - transparent color
Color_NameToColor()
p = 0
If Text.GetLength(color) = 9 Then
hex = Text.GetSubText(color, 2, 2)
Math_Hex2Dec()
a = dec
p = 4
ElseIf Text.GetLength(color) = 7 Then
a = 255
p = 2
EndIf
If 0 < p Then
dec = Math.Floor(a * opacity / 100)
Math_Dec2Hex()
color = "#" + hex + Text.GetSubTextToEnd(color, p)
EndIf
EndSub

Sub Colors_Init
'--
' Colors | Initialize 140 colors array
txt = "aliceblue,antiquewhite,aqua,aquamarine,"
txt = txt + "azure,beige,bisque,black,blanchedalmond,"
txt = txt + "blue,blueviolet,brown,burlywood,"
txt = txt + "cadetblue,chartreuse,chocolate,coral,"
txt = txt + "cornflowerblue,cornsilk,crimson,"
txt = txt + "cyan,darkblue,darkcyan,darkgoldenrod,"
txt = txt + "darkgray,darkgreen,darkkhaki,"
txt = txt + "darkmagenta,darkolivegreen,darkorange,"
txt = txt + "darkorchid,darkred,darksalmon,darkseagreen,"
txt = txt + "darkslateblue,darkslategray,"
txt = txt + "darkturquoise,darkviolet,deeppink,"
txt = txt + "deepskyblue,dimgray,dodgerblue,"
txt = txt + "firebrick,floralwhite,forestgreen,"
txt = txt + "fuchsia,gainsboro,ghostwhite,gold,"
txt = txt + "goldenrod,gray,green,greenyellow,"
txt = txt + "honeydew,hotpink,indianred,indigo,"
txt = txt + "ivory,khaki,lavender,lavenderblush,"
txt = txt + "lawngreen,lemonchiffon,lightblue,"
txt = txt + "lightcoral,lightcyan,lightgoldenrodyellow,"
txt = txt + "lightgray,lightgreen,lightpink,"
txt = txt + "lightsalmon,lightseagreen,lightskyblue,"
txt = txt + "lightslategray,lightsteelblue,"
txt = txt + "lightyellow,lime,limegreen,linen,"
txt = txt + "magenta,maroon,mediumaquamarine,mediumblue,"
txt = txt + "mediumorchid,mediumpurple,mediumseagreen,"
txt = txt + "mediumslateblue,mediumspringgreen,"
txt = txt + "mediumturquoise,mediumvioletred,midnightblue,"
txt = txt + "mintcream,mistyrose,moccasin,navajowhite,"
txt = txt + "navy,oldlace,olive,olivedrab,orange,"
txt = txt + "orangered,orchid,palegoldenrod,palegreen,"
txt = txt + "paleturquoise,palevioletred,papayawhip,"
txt = txt + "peachpuff,peru,pink,plum,powderblue,"
txt = txt + "purple,red,rosybrown,royalblue,saddlebrown,"
txt = txt + "salmon,sandybrown,seagreen,seashell,"
txt = txt + "sienna,silver,skyblue,slateblue,slategray,"
txt = txt + "snow,springgreen,steelblue,"
txt = txt + "tan,teal,thistle,tomato,turquoise,violet,"
txt = txt + "wheat,white,whitesmoke,yellow,yellowgreen"
delim = ","
Text_Split()
saved = GraphicsWindow.PenColor
n = Array.GetItemCount(arry)
For i = 1 To n
GraphicsWindow.PenColor = arry[i]
c = GraphicsWindow.PenColor
colors[arry[i]] = c
EndFor
GraphicsWindow.PenColor = saved
EndSub

Sub Group_Add
'--
' Group | add shapes to a group
' param name - group name
' param shX, shY, origin of shape array
' param shape[] - shape array
' param nGroup - number of group
' return nGroup - updated number of group
' return group - group array
Stack.PushValue("local", i)
Stack.PushValue("local", x)
Stack.PushValue("local", y)
nGroup = nGroup + 1
grp = ""
grp["name"] = name
grp["x"] = shX
grp["y"] = shY
grp["angle"] = 0
grp["dir"] = 1
Shapes_CalcWidthAndHeight()
grp["width"] = shWidth
grp["cx"] = shWidth / 2
grp["height"] = shHeight
s = 1
grp["scale"] = s
For i = 1 To Array.GetItemCount(shape)
shp = shape[i]
GraphicsWindow.PenWidth = shp["pw"] * s
If shp["pw"] > 0 Then
GraphicsWindow.PenColor = shp["pc"]
EndIf
If Text.IsSubText("rect|ell|tri|text", shp["func"]) Then
GraphicsWindow.BrushColor = shp["bc"]
EndIf
If shp["func"] = "rect" Then
shp["obj"] = Shapes.AddRectangle(shp["width"] * s, shp["height"] * s)
ElseIf shp["func"] = "ell" Then
shp["obj"] = Shapes.AddEllipse(shp["width"] * s, shp["height"] * s)
ElseIf shp["func"] = "tri" Then
shp["obj"] = Shapes.AddTriangle(shp["x1"] * s, shp["y1"] * s, shp["x2"] * s, shp["y2"] * s, shp["x3"] * s, shp["y3"] * s)
ElseIf shp["func"] = "line" Then
shp["obj"] = Shapes.AddLine(shp["x1"] * s, shp["y1"] * s, shp["x2"] * s, shp["y2"] * s)
ElseIf shp["func"] = "text" Then
If silverlight Then
fs = Math.Floor(shp["fs"] * 0.9)
Else
fs = shp["fs"]
EndIf
GraphicsWindow.FontSize = fs * s
GraphicsWindow.FontName = shp["fn"]
shp["obj"] = Shapes.AddText(shp["text"])
EndIf
x = shp["x"]
y = shp["y"]
shp["rx"] = x
shp["ry"] = y
If silverlight And Text.IsSubText("tri|line", shp["func"]) Then
alpha = Math.GetRadians(shp["angle"])
SB_RotateWorkaround()
shp["wx"] = x
shp["wy"] = y
EndIf
Shapes.Move(shp["obj"], shX + x * s, shY + y * s)
If Text.IsSubText("rect|ell|tri|text", shp["func"]) And (shp["angle"] <> 0) And (shp["angle"] <> "") Then
Shapes.Rotate(shp["obj"], shp["angle"])
EndIf
shape[i] = shp
EndFor
grp["shape"] = shape
group[nGroup] = grp
y = Stack.PopValue("local")
x = Stack.PopValue("local")
i = Stack.PopValue("local")
EndSub

Sub Group_Dump
'--
' Gourp | Dump a group for debug
' param group[i] - group to dump
grp = group[i]
TextWindow.WriteLine("name=" + grp["name"])
TextWindow.WriteLine("x=" + grp["x"])
TextWindow.WriteLine("y=" + grp["y"])
TextWindow.WriteLine("cx=" + grp["cx"])
TextWindow.WriteLine("width=" + grp["width"])
TextWindow.WriteLine("dir=" + grp["dir"])
shape = grp["shape"]
For j = 1 To Array.GetItemCount(shape)
TextWindow.WriteLine("shape[" + j + "]=" + WQ + shape[j] + WQ)
EndFor
EndSub

Sub Group_Flip
'--
' Group | Flip a group
' param group[i] - group to flip
' return group[i] - flipped group
grp = group[i]
gx = grp["x"]
gy = grp["y"]
shape = grp["shape"]
n = Array.GetItemCount(shape)
For angle = 20 To 180 Step 20
_a = Math.GetRadians(angle)
scaleX = Math.Cos(_a)
For j = 1 To n
shp = shape[j]
Shapes.Zoom(shp["obj"], Math.Abs(scaleX), 1)
cx = shp["x"] + shp["width"] / 2
cx = (cx - grp["cx"]) * scaleX + grp["cx"]
Shapes.Move(shp["obj"], cx - shp["width"] / 2 + gx, shp["y"] + gy)
If angle = 100 And shp["angle"] <> 0 Then
shp["angle"] = -shp["angle"]
Shapes.Rotate(shp["obj"], shp["angle"])
shape[j] = shp
EndIf
If angle = 180 Then
_x = Math.Floor((cx - shp["width"] / 2) * 100) / 100
shp["rx"] = _x
shp["x"] = _x
shape[j] = shp
EndIf
EndFor
Program.Delay(20)
EndFor
grp["dir"] = grp["dir"] * -1
grp["shape"] = shape
group[i] = grp
EndSub

Sub Group_Move
'--
' Group | Move a group
' param group[i] - group to move
' param x, y - position to move
' return group[i] - updated group
grp = group[i]
s = grp["scale"]
grp["x"] = x
grp["y"] = y
shape = grp["shape"]
n = Array.GetItemCount(shape)
For j = 1 To n
shp = shape[j]
If silverlight And Text.IsSubText("tri|line", shp["func"]) Then
_x = shp["wx"]
_y = shp["wy"]
Else
_x = shp["rx"]
_y = shp["ry"]
EndIf
Shapes.Move(shp["obj"], grp["x"] + _x * s, grp["y"] + _y * s)
EndFor
group[i] = grp
EndSub

Sub Math_Dec2Hex
'--
' Math | Convert decimal to hexadecimal
' param dec - decimal number
' returns hex - hexadecimal text
Stack.PushValue("local", dec)
hex = ""
While 0 < dec
digit = Math.Remainder(dec, 16)
dec = Math.Floor(dec / 16)
hex = Text.Append(Text.GetSubText("0123456789ABCDEF", digit + 1, 1), hex)
EndWhile
If hex = "" Then
hex = "0"
EndIf
dec = Stack.PopValue("local")
EndSub

Sub Math_Hex2Dec
' Math | Convert hexadecimal to decimal
' param hex - hexadecimal text
' return dec - decimal number
dec = 0
len = Text.GetLength(hex)
For ptr = 1 To len
dec = dec * 16 + Text.GetIndexOf("123456789ABCDEF", Text.GetSubText(hex, ptr, 1))
EndFor
EndSub

Sub SB_RotateWorkaround
'--
' Small Basic | Rotate workaround for Silverlight
' param shp - current shape
' param x, y - original coordinate
' param alpha - angle [radian]
' returns x, y - workaround coordinate
If shp["func"] = "tri" Then
x1 = -Math.Floor(shp["x3"] / 2)
y1 = -Math.Floor(shp["y3"] / 2)
ElseIf shp["func"] = "line" Then
x1 = -Math.Floor(Math.Abs(shp["x1"] - shp["x2"]) / 2)
y1 = -Math.Floor(Math.Abs(shp["y1"] - shp["y2"]) / 2)
EndIf
ox = x - x1
oy = y - y1
x = x1 * Math.Cos(alpha) - y1 * Math.Sin(alpha) + ox
y = x1 * Math.Sin(alpha) + y1 * Math.Cos(alpha) + oy
EndSub

Sub SB_Workaround
'--
' Small Basic | Workaround for Silverlight
' returns silverlight - "True" if in remote
color = GraphicsWindow.GetPixel(0, 0)
If Text.GetLength(color) > 7 Then
silverlight = "True"
msWait = 300
Else
silverlight = "False"
EndIf
EndSub

Sub Shapes_AddFunc
'--
' Shapes | Add a shape to shape array
iMax = iMax + 1
shape[iMax] = param
EndSub

Sub Shapes_CalcWidthAndHeight
'--
' Shapes | Calculate total width and height of shapes
' param shape[] - shape array
' return shWidth, shHeight - total size of shapes
For i = 1 To Array.GetItemCount(shape)
shp = shape[i]
If shp["func"] = "tri" Or shp["func"] = "line" Then
xmin = shp["x1"]
xmax = shp["x1"]
ymin = shp["y1"]
ymax = shp["y1"]
If shp["x2"] < xmin Then
xmin = shp["x2"]
EndIf
If xmax < shp["x2"] Then
xmax = shp["x2"]
EndIf
If shp["y2"] < ymin Then
ymin = shp["y2"]
EndIf
If ymax < shp["y2"] Then
ymax = shp["y2"]
EndIf
If shp["func"] = "tri" Then
If shp["x3"] < xmin Then
xmin = shp["x3"]
EndIf
If xmax < shp["x3"] Then
xmax = shp["x3"]
EndIf
If shp["y3"] < ymin Then
ymin = shp["y3"]
EndIf
If ymax < shp["y3"] Then
ymax = shp["y3"]
EndIf
EndIf
shp["width"] = xmax - xmin
shp["height"] = ymax - ymin
EndIf
If i = 1 Then
shWidth = shp["x"] + shp["width"]
shHeight = shp["y"] + shp["height"]
Else
If shWidth < shp["x"] + shp["width"] Then
shWidth = shp["x"] + shp["width"]
EndIf
If shHeight < shp["y"] + shp["height"] Then
shHeight = shp["y"] + shp["height"]
EndIf
EndIf
shape[i] = shp
EndFor
EndSub

Sub Text_Split
'--
' Text | Split text to array by delimiter
' param txt - to split
' param delim - delimiter
' return arry - splitted
len = Text.GetLength(txt)
p = 1
n = 0
d = Text.GetIndexOf(Text.GetSubTextToEnd(txt, p), delim)
While (0 < d) And (p <= len)
n = n + 1
arry[n] = Text.GetSubText(txt, p, d - 1)
p = p + d
d = Text.GetIndexOf(Text.GetSubTextToEnd(txt, p), delim)
EndWhile
n = n + 1
arry[n] = Text.GetSubTextToEnd(txt, p)
EndSub