Microsoft Small Basic

Program Listing: TGR736
' Happy Halloween 2017
' Copyright © 2017 Nonki Takahashi. The MIT License.
' Last update 2017-10-30

GraphicsWindow.Title = "Happy Halloween 2017"
Init()
' draw sky
param = "x=0;y=0;angle=90;0=Black;50=Indigo;100=DarkOrange;"
param["width"] = gw
param["height"] = gh - 60
GW_FillLinearGradientRectangle()
' draw ground
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillRectangle(0, gh - 60, gw, 60)
' draw moon
GraphicsWindow.BrushColor = "White"
GraphicsWindow.FillEllipse(gw - 90, 20, 60, 60)
' draw cross
GraphicsWindow.PenWidth = 15
GraphicsWindow.PenColor = "Black"
GraphicsWindow.DrawLine(50, gh - 150, 70, gh - 50)
GraphicsWindow.DrawLine(30, gh - 120, 80, gh - 130)
' draw pumpkin
y = gh - 100
For x = 150 To 310 Step 80
DrawPumpkin()
EndFor
' draw cat
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillEllipse(gw - 100, gh - 110, 40, 100)
GraphicsWindow.FillEllipse(gw - 100, gh - 130, 40, 30)
GraphicsWindow.FillTriangle(gw - 90, gh - 140, gw - 100, gh - 115, gw - 80, gh - 115)
GraphicsWindow.FillTriangle(gw - 70, gh - 140, gw - 80, gh - 115, gw - 60, gh - 115)
GraphicsWindow.BrushColor = "Indigo"
GraphicsWindow.FillEllipse(gw - 94, gh - 120, 9, 8)
GraphicsWindow.FillEllipse(gw - 76, gh - 120, 9, 8)
GraphicsWindow.BrushColor = "#111111"
GraphicsWindow.FillEllipse(gw - 84, gh - 113, 6, 4)
GraphicsWindow.PenWidth = 8
GraphicsWindow.PenColor = "Black"
param = "a=15;b=30;a1=0;a2=90;da=5;ct=Round;"
param["x"] = gw - 60
param["y"] = gh - 95
GW_DrawArc()
' draw caption
GraphicsWindow.BrushColor = "DarkOrange"
GraphicsWindow.FontName = "Trebuchet MS"
GraphicsWindow.FontSize = 50
GraphicsWindow.DrawText(30, 20, "Happy Halloween!")

Sub DrawPumpkin
GraphicsWindow.BrushColor = "Black"
For i = 1 To 4
GraphicsWindow.FillEllipse(x + (i - 1) * 12, y, 24, 50)
EndFor
GraphicsWindow.BrushColor = "DarkOrange"
GraphicsWindow.FillEllipse(x + 10, y + 20, 40, 20)
GraphicsWindow.BrushColor = "Black"
GraphicsWindow.FillEllipse(x + 10, y + 18, 40, 14)
GraphicsWindow.FillRectangle(x + 15, y + 29, 5, 5)
GraphicsWindow.FillRectangle(x + 27, y + 30, 5, 5)
GraphicsWindow.FillRectangle(x + 39, y + 29, 5, 5)
GraphicsWindow.FillRectangle(x + 21, y + 35, 5, 5)
GraphicsWindow.FillRectangle(x + 33, y + 35, 5, 5)
GraphicsWindow.BrushColor = "DarkOrange"
GraphicsWindow.FillTriangle(x + 19, y + 12, x + 12, y + 22, x + 27, y + 22)
GraphicsWindow.FillTriangle(x + 40, y + 12, x + 33, y + 22, x + 48, y + 22)
EndSub

Sub Init
gw = 598
gh = 428
GraphicsWindow.Width = gw
GraphicsWindow.Height = gh
Colors_Init()
EndSub

Sub GW_DrawArc
' GraphicsWindow | Draw arc
' param["x"] - center x coordinate [px]
' param["y"] - center y coordinate [px]
' param["r"] - radius [px]
' param["a1"] - start angle [°]
' param["a2"] - end angle [°]
' param["da"] - delta (step) angle [°]
' param["ct"] - cap type ("Round" for round, otherwise for flat)
Stack.PushValue("local", local)
Stack.PushValue("local", a)
local["pw"] = GraphicsWindow.PenWidth
local["bc"] = GraphicsWindow.BrushColor
GraphicsWindow.BrushColor = GraphicsWindow.PenColor
For a = param["a1"] To param["a2"] Step param["da"]
local["rad"] = Math.GetRadians(a)
local["x2"] = param["x"] + param["a"] * Math.Cos(local["rad"])
local["y2"] = param["y"] + param["b"] * Math.Sin(local["rad"])
If param["a1"] < a Then
GraphicsWindow.DrawLine(local["x1"], local["y1"], local["x2"], local["y2"])
EndIf
If ((param ["a1"] < a) And (a < param["a2"])) Or Text.ConvertToLowerCase(param["ct"]) = "round" Then
GraphicsWindow.PenWidth = 0
GraphicsWindow.FillEllipse(local["x2"] - local["pw"] / 2, local["y2"] - local["pw"] / 2, local["pw"], local["pw"])
GraphicsWindow.PenWidth = local["pw"]
EndIf
local["x1"] = local["x2"]
local["y1"] = local["y2"]
EndFor
GraphicsWindow.BrushColor = local["bc"]
a = Stack.PopValue("local")
local = Stack.PopValue("local")
EndSub

Sub GW_FillLinearGradientRectangle
' GraphicsWindow | Fill linear gradient rectangle
' param["x"], param["y"] - position of the rectangle
' param["width"], param["height"] - size of the rectangle
' param["angle"] - angle of gradient
' param["0"]..param["100"] - 0% to 100% colors
x0 = param["x"]
y0 = param["y"]
width = param["width"]
height = param["height"]
angle = param["angle"]
n = Array.GetItemCount(param)
index = Array.GetAllIndices(param)
color = ""
For i = 1 To n
If index[i] + 0 = index[i] And 0 <= index[i] And index[i] <= 100 Then
color[index[i]] = param[index[i]]
EndIf
EndFor
If color[0] = "" Then
color[0] = GraphicsWindow.BrushColor
EndIf
If color[100] = "" Then
color[100] = GraphicsWindow.BrushColor
EndIf
n = Array.GetItemCount(color)
index = Array.GetAllIndices(color)
a45 = Math.Remainder(angle, 45)
If 0 <= angle And angle < 45 Then
w45 = width
h45 = height
ElseIf 45 <= angle And angle < 90 Then
w45 = height
h45 = width
a45 = 45 - a45
ElseIf 90 <= angle And angle < 135 Then
w45 = height
h45 = width
ElseIf 135 <= angle And angle < 180 Then
w45 = width
h45 = height
a45 = 45 - a45
ElseIf 180 <= angle And angle < 225 Then
w45 = width
h45 = height
ElseIf 225 <= angle And angle < 270 Then
w45 = height
h45 = width
a45 = 45 - a45
ElseIf 270 <= angle And angle < 315 Then
w45 = height
h45 = width
ElseIf 315 <= angle And angle < 360 Then
w45 = width
h45 = height
a45 = 45 - a45
EndIf
If a45 = 0 Then
For x = 0 To w45
percent = Math.Floor(x * 100 / w45)
Color_PercentToRGB()
x1 = x
y1 = 0
x2 = x
y2 = h45
GW_DrawLine()
EndFor
ElseIf 0 < a45 And a45 <= 45 Then
tan = Math.Tan(Math.GetRadians(a45))
If h45 <= w45 Then
dx = Math.Floor(h45 * tan)
For x = 0 To dx
percent = Math.Floor(x * 100 / (w45 + dx))
Color_PercentToRGB()
x1 = x
y1 = 0
x2 = 0
y2 = Math.Floor(x / tan)
GW_DrawLine()
EndFor
For x = dx To w45
percent = Math.Floor(x * 100 / (w45 + dx))
Color_PercentToRGB()
x1 = x
y1 = 0
x2 = x - dx
y2 = h45
GW_DrawLine()
EndFor
For x = w45 To w45 + dx
percent = Math.Floor(x * 100 / (w45 + dx))
Color_PercentToRGB()
x1 = x - dx
y1 = h45
x2 = w45
y2 = Math.Floor((x - w45) / tan)
GW_DrawLine()
EndFor
Else ' width < height
dx = Math.Floor(h45 * tan - w45)
dy = Math.Floor(w45 / tan)
For x = 0 To w45
percent = Math.Floor(x * 100 / (2 * w45 + dx))
Color_PercentToRGB()
x1 = x
y1 = 0
x2 = 0
y2 = Math.Floor(x / tan)
GW_DrawLine()
EndFor
For x = 0 To dx
percent = Math.Floor((x + w45) * 100 / (2 * w45 + dx))
Color_PercentToRGB()
x1 = w45
y1 = Math.Floor(x / tan)
x2 = 0
y2 = y1 + dy
GW_DrawLine()
EndFor
dy = h45 - Math.Floor(w45 / tan)
For x = 0 To w45
percent = Math.Floor((x + w45 + dx) * 100 / (2 * w45 + dx))
Color_PercentToRGB()
x1 = x
y1 = h45
x2 = w45
y2 = dy + Math.Floor(x / tan)
GW_DrawLine()
EndFor
EndIf
EndIf
EndSub

Sub GW_DrawLine
' GraphicsWindow | Draw line
' param r, g, b - red, green blue
' param angle
' param x1, y1, x2, y2 - edges of line
GraphicsWindow.PenColor = GraphicsWindow.GetColorFromRGB(r, g, b)
If 0 <= angle And angle < 45 Then
GraphicsWindow.DrawLine(x1 + x0, y1 + y0, x2 + x0, y2 + y0)
ElseIf 45 <= angle And angle < 90 Then
GraphicsWindow.DrawLine(y1 + x0, x1 + y0, y2 + x0, x2 + y0)
ElseIf 90 <= angle And angle < 135 Then
GraphicsWindow.DrawLine(-y1 + x0 + width, x1 + y0, -y2 + x0 + width, x2 + y0)
ElseIf 135 <= angle And angle < 180 Then
GraphicsWindow.DrawLine(-x1 + x0 + width, y1 + y0, -x2 + x0 + width, y2 + y0)
ElseIf 180 <= angle And angle < 225 Then
GraphicsWindow.DrawLine(-x1 + x0 + width, -y1 + y0 + height, -x2 + x0 + width, -y2 + y0 + height)
ElseIf 225 <= angle And angle < 270 Then
GraphicsWindow.DrawLine(-y1 + x0 + width, -x1 + y0 + height, -y2 + x0 + width, -x2 + y0 + height)
ElseIf 270 <= angle And angle < 315 Then
GraphicsWindow.DrawLine(y1 + x0, -x1 + y0 + height, y2 + x0, -x2 + y0 + height)
ElseIf 315 <= angle And angle < 360 Then
GraphicsWindow.DrawLine(x1 + x0, -y1 + y0 + height, x2 + x0, -y2 + y0 + height)
EndIf
EndSub

Sub Color_PercentToRGB
' Color | Convert percent to RGB
' param percent - percent
' param color[] - color table indexed percent
' param n - item count of color[]
' param index[] - all indices of color[]
For i = 1 To n
p1 = index[i]
If index[i] = percent Then
p2 = index[i]
i = n + 1 ' break
ElseIf index[i] < percent And percent < index[i + 1] Then
p2 = index[i + 1]
i = n + 1 ' break
EndIf
EndFor
c = color[p1]
Color_ColorToRGB()
If p1 <> p2 Then
r1 = r
g1 = g
b1 = b
c = color[p2]
Color_ColorToRGB()
r2 = r
g2 = g
b2 = b
r = Math.Floor(r1 + (r2 - r1) * (percent - p1) / (p2 - p1))
g = Math.Floor(g1 + (g2 - g1) * (percent - p1) / (p2 - p1))
b = Math.Floor(b1 + (b2 - b1) * (percent - p1) / (p2 - p1))
EndIf
EndSub

Sub Color_ColorToRGB
' Color | Convert color to RGB
' param c - color
' returns r, g, b - red, green and blue values
If Text.StartsWith(c, "#") Then
c = Text.ConvertToUpperCase(c)
Else
c = Text.ConvertToLowerCase(c)
c = colors[c]
EndIf
sHex = Text.GetSubText(c, 2, 2)
Math_Hex2Dec()
r = iDec
sHex = Text.GetSubText(c, 4, 2)
Math_Hex2Dec()
g = iDec
sHex = Text.GetSubText(c, 6, 2)
Math_Hex2Dec()
b = iDec
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 Math_Hex2Dec
' Math | Convert hexadecimal to decimal
' param sHex
' returns iDec
iDec = 0
iLen = Text.GetLength(sHex)
For iPtr = 1 To iLen
iDec = iDec * 16 + Text.GetIndexOf("0123456789ABCDEF", Text.GetSubText(sHex, iPtr, 1)) - 1
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