Microsoft Small Basic

Program Listing: TKW252-9
' Aquarium
' Version 0.6
' Copyright © 2012-2019 Nonki Takahashi. All rights reserved.
'
' History:
' 0.6 2019-04-28 Workaround for Silverlight. (TKW252-9)
' 0.5 2019-04-20 Refactored. (TKW252-8)
' 0.4 2017-10-04 Minor change.
' 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.6"
Not = "False=True;True=False;"
WQ = Text.GetCharacter(34)
SB_Workaround()
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 = "RoyalBlue Fish"
name = caption
wCaption = 88 + 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
colorBrush = "RoyalBlue"
c = colorBrush
rate = 0.4
Color_Blacken()
colorPen = c
param["pw"] = 2
fAngle = ""
For i = 1 To nFish
shape = ""
iMax = 0
Stack.PushValue("local", x)
Stack.PushValue("local", y)
deltaAngle[i] = Math.GetRandomNumber(5) + 5
fAngle[i] = Math.GetRandomNumber(360) - 1
_angle = Math.GetRadians(fAngle[i])
shX = gw / 2 + width * 0.4 * Math.Cos(_angle) - wFish * 2 / 3
shY = y + height - hFish - Math.GetRandomNumber(depth - hFish)
opacity = 60
c = colorPen
Color_SetAlpha()
param["pc"] = c ' tail color
c = colorBrush
Color_SetAlpha()
param["bc"] = c ' 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()
angle = fAngle[i] + 90
Group_Flip()
EndFor
EndSub

Sub MoveFish
Stack.PushValue("local", x)
Stack.PushValue("local", y)
xo = x
yo = y
For i = 1 To nFish
grp = group[i]
fAngle[i] = fAngle[i] + deltaAngle[i]
If 360 <= fAngle[i] Then
fAngle[i] = fAngle[i] - 360
EndIf
_angle = Math.GetRadians(fAngle[i])
x = gw / 2 + width * 0.4 * Math.Cos(_angle) - wFish * 2 / 3
y = grp["y"]
Group_Move()
angle = fAngle[i] + 90
Group_Flip()
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 = "LightSeaGreen"
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_Blacken
' Color | blacken given color
' param c - given color
' param rate - 0..1
' return c - color blackened
Stack.PushValue("local", r)
rate = rate
Color_NameToColor()
Color_ColorToRGB()
r = Math.Floor(r * (1 - rate))
g = Math.Floor(g * (1 - rate))
b = Math.Floor(b * (1 - rate))
c = GraphicsWindow.GetColorFromRGB(r, g, b)
r = Stack.PopValue("local")
EndSub

Sub Color_ColorToRGB
' Color | convert color To RGB values
' param c - "#rrggbb" (hexadecimal values)
' return r, g, b - RGB values 0..255
If Text.GetLength(c) = 9 Then
_alpha = 2
Else
_alpha = 0
EndIf
sR = Text.GetSubText(c, _alpha + 2, 2)
sG = Text.GetSubText(c, _alpha + 4, 2)
sB = Text.GetSubText(c, _alpha + 6, 2)
hex = sR
Math_Hex2Dec()
r = dec
hex = sG
Math_Hex2Dec()
g = dec
hex = sB
Math_Hex2Dec()
b = dec
EndSub

Sub Color_NameToColor
' Color | convert color name to color
' param c - color name
' returns c -"#rrggbb"
If Text.StartsWith(c, "#") And 6 < Text.GetLength(c) Then
c = Text.ConvertToUpperCase(c)
Else
Stack.PushValue("local", GraphicsWindow.PenColor)
GraphicsWindow.PenColor = c
c = GraphicsWindow.PenColor
GraphicsWindow.PenColor = Stack.PopValue("local")
EndIf
EndSub

Sub Color_SetAlpha
' Color | set alpha
' param c - color to set alpha blending
' param opacity - opacity
' return c - transparent color
Color_NameToColor()
p = 0
If Text.GetLength(c) = 9 Then
hex = Text.GetSubText(c, 2, 2)
Math_Hex2Dec()
a = dec
p = 4
ElseIf Text.GetLength(c) = 7 Then
a = 255
p = 2
EndIf
If 0 < p Then
dec = Math.Floor(a * opacity / 100)
Math_Dec2Hex()
c = "#" + hex + Text.GetSubTextToEnd(c, p)
EndIf
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
' Group | 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
' param angle - to flip
' return group[i] - flipped group
Stack.PushValue("local", angle)
grp = group[i]
gx = grp["x"]
gy = grp["y"]
shape = grp["shape"]
n = Array.GetItemCount(shape)
angle = Math.Remainder(angle, 360)
If angle < 0 Then
angle = angle + 360
EndIf
If (angle <= 90) Or (270 < angle) Then
sign = 1
Else
sign = -1
EndIf
If (grp["flip"] <= 90) Or (270 < grp["flip"]) Then
lastSign = 1
Else
lastSign = -1
EndIf
_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
fx = (cx - grp["cx"]) * scaleX + grp["cx"]
Shapes.Move(shp["obj"], fx - shp["width"] / 2 + gx, shp["y"] + gy)
If sign <> lastSign Then
If shp["angle"] <> "" Then
shp["angle"] = -shp["angle"]
Shapes.Rotate(shp["obj"], shp["angle"])
EndIf
_x = Math.Floor((cx - shp["width"] / 2) * 100) / 100
shp["rx"] = _x
shp["x"] = _x
shape[j] = shp
EndIf
EndFor
grp["flip"] = angle
grp["shape"] = shape
group[i] = grp
angle = Stack.PopValue("local")
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