September 12, 2010
Several years ago I developed a somewhat simple program in 16 bit Borland C (last compiled in 1994, so it pre-dates graphical web pages) that was optimized for 386 and 486 computers which sported VGA graphics but was also able to work with EGA graphics cards. The simple program, after creating 18 images on the fly that simulated a rotating basketball, generated X and Y coordinates using a specially crafted formula that was capable of producing X and Y cordinates in a circular pattern. With slightly different inputs the same formula produced a slightly out of round circular pattern that eventually might have generated straight line patterns. With a slight adjustment to the inputs the same formula produced W patterns within a bounded box. Keyboard input allowed the user to specify adjusted inputs as the program executed. The C program looked like this:
#include <graphics.h> // for graphics functions #include <conio.h> // for kbhit() #include <math.h> #include <dos.h> #define LEFT 100 // boundary of rectangle #define TOP 100 #define RADIUS 20 // radius of ball #define pi 3.14159276 #define L_ARROW 75 #define R_ARROW 77 #define U_ARROW 72 #define D_ARROW 80 #define ESC 27 void main(void) { int driver, mode; // for initgraph() float x, y, dx, dy, i, sped, temp, x1, x2, y1, y2, sta1, sta2, ena1, ena2, rad; // ball coordinates int imgnum, tim, del; char key; unsigned char ballbuff[10][5000]; // buffer for ball image driver = DETECT; // auto detect // set graphics mode initgraph(&driver, &mode, "c:\\bc4\\bgi"); x = LEFT + RADIUS; // designate center where ball is created y = TOP + RADIUS; for (i =0; i <180; i = i + 18) // create basketball rotation images { setcolor(RED); setfillstyle(SOLID_FILL, RED); circle(x, y, RADIUS); floodfill(x, y, RED); setcolor(BLACK); rad = i / (2 * pi); x1 = x + 30 * cos(rad); x2 = x - 30 * cos(rad); y1 = y + 30 * sin(rad); y2 = y - 30 * sin(rad); sta1 = (180 + i -62); ena1 = (180 + i + 42); sta2 = (i -62); ena2 = (i + 42); if ((i/36) != int(i/36)) // must be included to swap arcs { temp = sta1; sta1 = sta2; sta2 = temp; temp = ena1; ena1 = ena2; ena2 = temp; } ellipse (x1, y1, sta1, ena1, RADIUS , RADIUS); ellipse (x2, y2, sta2, ena2, RADIUS , RADIUS); line (x - cos(rad + pi/2) * RADIUS, y - sin(rad + pi/2) * RADIUS, x + cos(rad + pi/2) * RADIUS, y + sin(rad + pi/2) * RADIUS); // pickup image getimage(x-RADIUS-20, y-RADIUS-20, x+RADIUS+20, y+RADIUS+20, ballbuff[i/18]); // clear screen setcolor(WHITE); rectangle(-1,-1, 640, 480); setfillstyle(SOLID_FILL, BLACK); floodfill(100,100, WHITE); } imgnum = 10; // load first position + 1 tim = 0; // set delay to zero dx = .1; // set constant in x equation dy = .1; sped = 1; del = 1; while ( key != ESC ) { if (kbhit()) { key = getch(); if (key == 61) del++; // = key pressed if (key == 43) del++; // + key pressed if (key == 45) del--; // - key pressed if (key == 47) sped = sped +.1; // / key pressed if (key == 92) sped = sped -.1; // \ key pressed if (key == 0) // place image on screen switch(getch()) { case L_ARROW: putimage(x-RADIUS, y-RADIUS, ballbuff[imgnum], XOR_PUT); dx = dx -.01; break; case R_ARROW: putimage(x-RADIUS, y-RADIUS, ballbuff[imgnum], XOR_PUT); dx = dx + .01; break; case U_ARROW: putimage(x-RADIUS, y-RADIUS, ballbuff[imgnum], XOR_PUT); dy = dy + .01; break; case D_ARROW: putimage(x-RADIUS, y-RADIUS, ballbuff[imgnum], XOR_PUT); dy = dy -.01; break; case ESC: key = 27; break; } } tim = tim + sped; x = (sin(dx * tim)*100) + 300; y = (cos(dy * tim)* 100) + 300; imgnum--; // cycle through images if (imgnum == -1) imgnum = 9; putimage(x-RADIUS, y-RADIUS, ballbuff[imgnum], COPY_PUT); // move ball across screen // to make ball move rapidly increase + // set height on screen delay(del); // make delay smaller for slow computers } getch(); closegraph(); }
If you still have a 16 bit Borland C compiler (my copy is in a huge box on the bottom self of my bookcase), feel free to compile the above program to see it in action. The original compiled C program, last compiled in 1994, may be downloaded here: OrbitBall2.zip (save as OrbitBall.zip and extract the files – download might not work). The original program is confirmed to work on 32 bit Windows 95 through Windows XP (in DOS full screen mode), but definitely will not work on 64 bit Windows, even in a virtual machine (it also failed to run on a 32 bit Windows 7 netbook).
You are probably thinking, “Neat, but what does that have to do with Oracle?”. It might be interesting to produce a modernized version of the above program. We could use a simple SQL statement like the following to generate 3,600 X and Y coordinates, much like the X and Y coordinates that were generated by the above C program (note that the COS function is typically used to derive X values (using mathematical cosine) and the SIN function is typically used to derive Y values – the functions were swapped so that the X and Y cordinates start at the bottom-center of the screen). The SQL follows:
SELECT ROUND((SIN(DX * (SPEED * COUNTER)) * WIDTH/2) + WIDTH/2) X, ROUND((COS(DY * (SPEED * COUNTER)) * HEIGHT/2) + HEIGHT/2) Y FROM (SELECT 0.1 DX, 0.1 DY, 1 SPEED, 1 DELAY, 300 WIDTH, 300 HEIGHT, LEVEL COUNTER FROM DUAL CONNECT BY LEVEL<=3600);
Now we have a slight problem, how do we present the X and Y coordinate points produced by the above SQL statement? We need some sort of object to track the X and Y coordinate pairs. Drawn basketballs might work, but instead I will use these pictures (created with Microsoft Power Point 2010):
To display the round oak pictures, we will put together a VBS script to build a web page on the fly, cycling through the above eight pictures (every two X,Y coordinate pairs cause the displayed picture to change). Much like the original program, we will allow the user to control the input parameters as the program runs. Each time the parameters are adjusted, 3,600 new X,Y coordinate points are retrieved from the database into an array – this allows the VBS script to continue at the next X,Y coordinate pair, rather than starting at the beginning every time the parameters are adjusted.
Option Explicit Const adCmdText = 1 Const adCmdStoredProc = 4 Const adParamInput = 1 Const adVarNumeric = 139 Const adBigInt = 20 Const adDecimal = 14 Const adDouble = 5 Const adInteger = 3 Const adLongVarBinary = 205 Const adNumeric = 131 Const adSingle = 4 Const adSmallInt = 2 Const adTinyInt = 16 Const adUnsignedBigInt = 21 Const adUnsignedInt = 19 Const adUnsignedSmallInt = 18 Const adUnsignedTinyInt = 17 Const adDate = 7 Const adDBDate = 133 Const adDBTimeStamp = 135 Const adDBTime = 134 Const adVarChar = 200 Const adUseClient = 3 Dim dbDatabase Dim snpData Dim comData Dim varData Dim objIE Dim strUsername 'Username Dim strPassword 'Password Dim strDatabase 'SID name from tnsnames.ora startup Sub startup() Dim strSQL Dim strHTML Dim objOrbitBall Dim objOrbitBallPic Dim objCommand Dim objSettings Dim i Dim intQuit 'Fire up Internet Explorer Set objIE = CreateObject("InternetExplorer.Application") objIE.Left = 0 objIE.Top = 0 objIE.Width = 930 objIE.Height = 820 objIE.StatusBar = True objIE.MenuBar = False objIE.Toolbar = False objIE.Navigate "about:blank" objIE.Document.Title = "The SQL to the Orbiting Ball" objIE.Visible = True 'The data entry area strHTML = "<div style=""position: absolute;width: 180px; height: 200px;top: 10px;left: 710px;"">" & vbCrLf strHTML = strHTML & "<input type=text id=txtCommand value="""" size=""1""><br>" & vbCrLf strHTML = strHTML & "<font size=1><b>+ Increase Delay (Not Used)<br>" & vbCrLf strHTML = strHTML & "- Decrease Delay (Not Used)<br>" & vbCrLf strHTML = strHTML & "/ Increase Rotation Speed<br>" & vbCrLf strHTML = strHTML & "\ Decrease Rotation Speed<br>" & vbCrLf strHTML = strHTML & "D Increase Rotation Speed X Axis<br>" & vbCrLf strHTML = strHTML & "A Decrease Rotation Speed X Axis<br>" & vbCrLf strHTML = strHTML & "W Increase Rotation Speed Y Axis<br>" & vbCrLf strHTML = strHTML & "S Decrease Rotation Speed Y Axis<br>" & vbCrLf strHTML = strHTML & "L Increase Width X Axis<br>" & vbCrLf strHTML = strHTML & "J Decrease Width X Axis<br>" & vbCrLf strHTML = strHTML & "I Increase Height Y Axis<br>" & vbCrLf strHTML = strHTML & "K Decrease Height Y Axis<br>" & vbCrLf strHTML = strHTML & "(space) Restart at 0<br>" & vbCrLf strHTML = strHTML & "X Exit</b></font>" & vbCrLf strHTML = strHTML & "</div>" & vbCrLf 'The current orbit information strHTML = strHTML & "<div id=""Settings"" style=""position: absolute;width: 180px; height: 100px;top: 600px;left: 710px;""> </div>" strHTML = strHTML & "<IMG ID=""picOrbitBall"" style=""position: absolute;"" src=""https://hoopercharles.files.wordpress.com/2010/09/sqlorbitingball0.png"">" & vbCrLf objIE.Document.Body.InnerHTML = strHTML 'The sleep here is only necessary if the database connections happen very quickly 'Wscript.Sleep 500 Set dbDatabase = CreateObject("ADODB.Connection") Set snpData = CreateObject("ADODB.Recordset") Set comData = CreateObject("ADODB.Command") 'Database configuration strUsername = "MyUsername" strPassword = "MyPassword" strDatabase = "MyDB" On Error Resume Next dbDatabase.ConnectionString = "Provider=OraOLEDB.Oracle;Data Source=" & strDatabase & ";User ID=" & strUsername & ";Password=" & strPassword & ";" dbDatabase.open 'Should verify that the connection attempt was successful, but I will leave that for someone else to code strSQL = "SELECT" & vbCrLf strSQL = strSQL & " ROUND((SIN(DX * (SPEED * COUNTER)) * WIDTH/2) + WIDTH/2) X," & vbCrLf strSQL = strSQL & " ROUND((COS(DY * (SPEED * COUNTER)) * HEIGHT/2) + HEIGHT/2) Y" & vbCrLf strSQL = strSQL & "FROM" & vbCrLf strSQL = strSQL & " (SELECT" & vbCrLf strSQL = strSQL & " ? DX," & vbCrLf strSQL = strSQL & " ? DY," & vbCrLf strSQL = strSQL & " ? SPEED," & vbCrLf strSQL = strSQL & " ? DELAY," & vbCrLf strSQL = strSQL & " ? WIDTH," & vbCrLf strSQL = strSQL & " ? HEIGHT," & vbCrLf strSQL = strSQL & " LEVEL COUNTER" & vbCrLf strSQL = strSQL & " FROM" & vbCrLf strSQL = strSQL & " DUAL" & vbCrLf strSQL = strSQL & " CONNECT BY" & vbCrLf strSQL = strSQL & " LEVEL<=3600)" With comData 'Set up the command properties .CommandText = strSQL .CommandType = adCmdText .CommandTimeout = 30 .ActiveConnection = dbDatabase 'Add the bind variables .Parameters.Append .CreateParameter("dx", adDouble, adParamInput, 30) .Parameters.Append .CreateParameter("dy", adDouble, adParamInput, 30) .Parameters.Append .CreateParameter("speed", adDouble, adParamInput, 30) .Parameters.Append .CreateParameter("delay", adDouble, adParamInput, 30) .Parameters.Append .CreateParameter("width", adDouble, adParamInput, 30) .Parameters.Append .CreateParameter("height", adDouble, adParamInput, 30) End With comData("dx") = 0.1 comData("dy") = 0.1 comData("speed") = 1 comData("delay") = 1 comData("width") = 600 comData("height") = 600 Set snpData = comData.Execute 'Retrieve up to 10,000 data points from Oracle varData = snpData.GetRows(10000) snpData.Close Set snpData = Nothing 'Allow faster access to these objects when executing in the loop Set objOrbitBall = objIE.Document.getElementById("picOrbitBall").Style Set objOrbitBallPic = objIE.Document.getElementById("picOrbitBall") Set objCommand = objIE.Document.All.txtCommand Set objSettings = objIE.Document.getElementById("Settings") 'Write out the current settings for the orbit objSettings.InnerText = "DX: " & comData("dx") & Chr(10) & "DY: " & comData("dy") & Chr(10) & _ "Speed: " & comData("speed") &Chr(10) & _ "Width: " & comData("width") & Chr(10) & "Height: " & comData("height") intQuit = False Do While intQuit = False For i = 0 To UBound(varData, 2) objOrbitBall.Left = CInt(varData(0, i)) objOrbitBall.Top = CInt(varData(1, i)) objOrbitBallPic.Src = "https://hoopercharles.files.wordpress.com/2010/09/sqlorbitingball" & cStr(i/2 Mod 8 ) & ".png" Wscript.Sleep 50 Select Case Left(objCommand.Value, 1) Case "=", "+" comData("delay") = comData("delay") + 1 Case "-" comData("delay") = comData("delay") - 1 Case "/" comData("speed") = comData("speed") + 0.1 Case "\" comData("speed") = comData("speed") - 0.1 Case "W", "w" comData("dy") = comData("dy") + 0.0005 Case "S", "s" comData("dy") = comData("dy") - 0.0005 Case "D", "d" comData("dx") = comData("dx") + 0.0005 Case "A", "a" comData("dx") = comData("dx") - 0.0005 Case "I", "i" comData("height") = comData("height") + 5 Case "K", "k" comData("height") = comData("height") - 5 Case "L", "l" comData("width") = comData("width") + 5 Case "J", "j" comData("width") = comData("width") - 5 Case "X", "x" intQuit = True Exit For Case " " 'Reset the loop from the beginning objCommand.Value = "" Exit For End Select If objCommand.Value <> "" Then objCommand.Value = "" Set snpData = comData.Execute 'Retrieve up to 10,000 data points from Oracle varData = snpData.GetRows(10000) snpData.Close Set snpData = Nothing 'Write out the current settings for the orbit objSettings.InnerText = "DX: " & comData("dx") & Chr(10) & "DY: " & comData("dy") & Chr(10) & _ "Speed: " & comData("speed") &Chr(10) & _ "Width: " & comData("width") & Chr(10) & "Height: " & comData("height") End If Next Loop objIE.quit dbDatabase.Close Set dbDatabase = Nothing Set objIE = Nothing End Sub
You may download the above script here: SQLOrbitingBall.vbs (save as SQLOrbitingBall.vbs).
A Circular Orbit:
————-
An Orbit that Changes from a Circular Orbit to a Straight Line:
————-
An Orbit where the Ball Bounces Between the Top and Bottom of the Window:
—————————
As written, the script assumes a minimum of 930 x 820 resolution (1080p resolution or greater should work without any problems). Adjust the script as necessary for lower resolution screens. The program written in C certainly is shorter than the moderized version of the program, and had a bit more wow factor prior to the widespread use of Windows and other graphical user interfaces.
Recent Comments