-' Program to render 3D pointcloud galaxy.\r
-' By Svjatoslav Agejenko.\r
-' Email: svjatoslav@svjatoslav.eu\r
-' Homepage: http://www.svjatoslav.eu\r
-'\r
-' Changelog:\r
-' 2003, Initial version.\r
-' 2024, Improved program readability using AI.\r
-'\r
-' User can navigate through the galaxy using mouse or keyboard controls.\r
-' To use mouse controls, program requires special TSR (terminate and stay resident)\r
-' program to be loaded. The TSR makes mouse input available to the QBasic program.\r
-'\r
-' Navigation controls:\r
-' - Press left and right mouse buttons simultaneously to move in X and Z axis.\r
-' - Press right mouse button to move in Y axis.\r
-' - Use keyboard keys 'a', 'd', 'w', 's' to move in X and Z axis.\r
-' - Press 'q' to quit the program.\r
-\r
-DECLARE SUB temp ()\r
-DECLARE SUB mkGalaxy (x!, y!, z!)\r
-DECLARE SUB rndInit ()\r
-DECLARE FUNCTION rn! ()\r
-DECLARE SUB disp ()\r
-DECLARE SUB starText ()\r
-DECLARE SUB control ()\r
-DECLARE SUB putByte (addr!, dat!)\r
-DECLARE SUB putWord (addr!, dat!)\r
-DECLARE FUNCTION getWord! (addr!)\r
-DECLARE FUNCTION getByte! (addr!)\r
-DECLARE SUB start ()\r
-DECLARE SUB animate ()\r
-\r
-DIM SHARED angle1, angle2, angle3\r
-\r
-DIM SHARED time\r
-\r
-DIM SHARED externalSegment, externalAddress\r
-\r
-DIM SHARED myX, myY, myZ\r
-DIM SHARED speedX, speedY, speedZ\r
-DIM SHARED buttonLeft, buttonRight\r
-DIM SHARED maxMove\r
-\r
-DIM SHARED zoom\r
-DIM SHARED randomValue(0 TO 10000)\r
-DIM SHARED randomPointer\r
-\r
-DIM SHARED pointX(1 TO 12000)\r
-DIM SHARED pointY(1 TO 12000)\r
-DIM SHARED pointZ(1 TO 12000)\r
-DIM SHARED pointColor(1 TO 12000)\r
-DIM SHARED numPoints\r
-\r
-DIM SHARED temporaryRegister(0 TO 10)\r
-\r
-newLine = 0\r
-newPage = 0\r
-\r
-start\r
-\r
-currentX = 0\r
-currentY = 0\r
-currentZ = 0\r
-\r
-numPoints = 0\r
-mkGalaxy 0, 0, 0\r
-1\r
-\r
-variableAngle = INT(RND * 3)\r
-\r
-SELECT CASE variableAngle\r
-CASE 0\r
- currentX = RND * 500 - 250\r
-CASE 1\r
- currentY = RND * 100 - 50\r
-CASE 2\r
- currentZ = RND * 500 - 250\r
-END SELECT\r
-\r
-control\r
-disp\r
-\r
-PCOPY 0, 1\r
-CLS\r
-GOTO 1\r
-\r
-SUB control\r
-\r
-IF getByte(8) <> 0 THEN\r
- putByte 8, 0\r
- xPosition = getWord(2)\r
- putWord 2, 0\r
- yPosition = getWord(4)\r
- putWord 4, 0\r
- button = getWord(6)\r
- putWord 6, 0\r
- buttonLeft = 0\r
- buttonRight = 0\r
- IF button = 1 THEN buttonLeft = 1\r
- IF button = 2 THEN buttonRight = 1\r
- IF button = 3 THEN buttonLeft = 1: buttonRight = 1\r
-\r
- ' Handle mouse movements\r
- IF buttonRight = 1 THEN\r
- IF buttonLeft = 1 THEN\r
- speedX = speedX + SIN(angle1) * yPosition / 4\r
- speedZ = speedZ - COS(angle1) * yPosition / 4\r
- GOTO 3\r
- END IF\r
- speedY = speedY + yPosition / 4\r
- 3\r
- yPosition = 0\r
- END IF\r
-\r
-END IF\r
-\r
-' Limit the position values to prevent overflow\r
-IF xPosition < -maxMove THEN xPosition = -maxMove\r
-IF xPosition > maxMove THEN xPosition = maxMove\r
-angle1 = angle1 - xPosition / 150\r
-\r
-IF yPosition < -maxMove THEN yPosition = -maxMove\r
-IF yPosition > maxMove THEN yPosition = maxMove\r
-angle2 = angle2 - yPosition / 150\r
-\r
-' Check for keyboard input\r
-keyInput$ = INKEY$\r
-\r
-IF keyInput$ = "a" THEN speedX = speedX - COS(angle1): speedZ = speedZ - SIN(angle1)\r
-IF keyInput$ = "d" THEN speedX = speedX + COS(angle1): speedZ = speedZ + SIN(angle1)\r
-IF keyInput$ = "w" THEN speedX = speedX - SIN(angle1): speedZ = speedZ + COS(angle1)\r
-IF keyInput$ = "s" THEN speedX = speedX + SIN(angle1): speedZ = speedZ - COS(angle1)\r
-IF keyInput$ = "q" THEN SYSTEM\r
-\r
-' Decelerate the movement\r
-speedX = speedX / 1.1\r
-speedY = speedY / 1.1\r
-speedZ = speedZ / 1.1\r
-\r
-myX = myX + speedX\r
-myZ = myZ + speedZ\r
-myY = myY + speedY\r
-\r
-END SUB\r
-\r
-SUB disp\r
-\r
-sinAngle1 = SIN(angle1)\r
-cosAngle1 = COS(angle1)\r
-sinAngle2 = SIN(angle2)\r
-cosAngle2 = COS(angle2)\r
-\r
-' Loop through all points to calculate and display their positions\r
-FOR pointIndex = 1 TO numPoints\r
-\r
- xCoordinate = pointX(pointIndex) - myX\r
- yCoordinate = pointY(pointIndex) - myY\r
- zCoordinate = pointZ(pointIndex) - myZ\r
-\r
- ' Rotate the points\r
- rotatedX = xCoordinate * cosAngle1 + zCoordinate * sinAngle1\r
- rotatedZ = zCoordinate * cosAngle1 - xCoordinate * sinAngle1\r
-\r
- rotatedY = yCoordinate * cosAngle2 + rotatedZ * sinAngle2\r
- finalZ = rotatedZ * cosAngle2 - yCoordinate * sinAngle2\r
-\r
- ' Draw points that are sufficiently close\r
- IF finalZ > 3 THEN\r
- screenX = rotatedX / finalZ * 130 + 160\r
- screenY = rotatedY / finalZ * 130 + 100\r
- PSET (screenX, screenY), pointColor(pointIndex)\r
- END IF\r
-\r
-NEXT pointIndex\r
-END SUB\r
-\r
-FUNCTION getByte (address)\r
-getByte = PEEK(externalAddress + address)\r
-END FUNCTION\r
-\r
-FUNCTION getWord (address)\r
-firstByte = PEEK(externalAddress + address)\r
-secondByte = PEEK(externalAddress + address + 1)\r
-\r
-combinedHex$ = HEX$(firstByte)\r
-IF LEN(combinedHex$) = 1 THEN combinedHex$ = "0" + combinedHex$\r
-IF LEN(combinedHex$) = 0 THEN combinedHex$ = "00"\r
-\r
-combinedValue = VAL("&H" + HEX$(secondByte) + combinedHex$)\r
-\r
-getWord = combinedValue\r
-END FUNCTION\r
-\r
-SUB mkGalaxy (localX, localY, localZ)\r
-\r
-randomAngle1 = rn * 10\r
-randomAngle2 = rn * 10\r
-\r
-galaxySin1 = SIN(randomAngle1)\r
-galaxyCos1 = COS(randomAngle1)\r
-galaxySin2 = SIN(randomAngle2)\r
-galaxyCos2 = COS(randomAngle2)\r
-\r
-randomPointer = 0\r
-size = 100\r
-piValue = 3.14\r
-spiralBarsMultiplier = 3\r
-\r
-FOR pointIndex = 1 TO 10000\r
-\r
- randomVariable = rn * 10\r
- distanceFromCenter = randomVariable * randomVariable / 30\r
-\r
- spiralOffset = rn * (11.5 - randomVariable) / 3\r
- halfSpiralOffset = spiralOffset / 2\r
-\r
- angleExponent = rn * (distanceFromCenter / 2) / spiralBarsMultiplier * 2\r
- spiralBarAngle = 2 * piValue / spiralBarsMultiplier * INT(rn * spiralBarsMultiplier)\r
-\r
- xCoordinate = (SIN(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size\r
- zCoordinate = (COS(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size\r
- yCoordinate = (rn * spiralOffset - halfSpiralOffset) * size\r
-\r
- rotatedX = xCoordinate * galaxyCos1 + zCoordinate * galaxySin1\r
- rotatedZ = zCoordinate * galaxyCos1 - xCoordinate * galaxySin1\r
-\r
- rotatedY = yCoordinate * galaxyCos2 + rotatedZ * galaxySin2\r
- finalZ = rotatedZ * galaxyCos2 - yCoordinate * galaxySin2\r
-\r
- numPoints = numPoints + 1\r
-\r
- pointX(numPoints) = rotatedX + localX\r
- pointY(numPoints) = rotatedY + localY\r
- pointZ(numPoints) = finalZ + localZ\r
- pointColor(numPoints) = INT(RND * 15) + 1\r
-NEXT pointIndex\r
-\r
-END SUB\r
-\r
-SUB mouseDemo\r
-\r
-currentX = 150\r
-currentY = 100\r
-maxMove = 50\r
-100\r
-frameCounter = frameCounter + 1\r
-\r
-LOCATE 1, 1\r
-PRINT currentX, currentY\r
-PRINT frameCounter\r
-\r
-CIRCLE (currentX, currentY), 10, 0\r
-xPosition = getWord(2)\r
-putWord 2, 0\r
-yPosition = getWord(4)\r
-putWord 4, 0\r
-\r
-IF xPosition < -maxMove THEN xPosition = -maxMove\r
-IF xPosition > maxMove THEN xPosition = maxMove\r
-currentX = currentX + xPosition\r
-\r
-IF yPosition < -maxMove THEN yPosition = -maxMove\r
-IF yPosition > maxMove THEN yPosition = maxMove\r
-currentY = currentY + yPosition\r
-\r
-CIRCLE (currentX, currentY), 10, 10\r
-\r
-SOUND 0, .05\r
-GOTO 100\r
-\r
-END SUB\r
-\r
-SUB putByte (address, dataByte)\r
-\r
-POKE (externalAddress + address), dataByte\r
-END SUB\r
-\r
-SUB putWord (address, dataWord)\r
-\r
-hexValue$ = HEX$(dataWord)\r
-\r
-2\r
-IF LEN(hexValue$) < 4 THEN hexValue$ = "0" + hexValue$: GOTO 2\r
-\r
-firstByteValue = VAL("&H" + LEFT$(hexValue$, 2))\r
-secondByteValue = VAL("&H" + RIGHT$(hexValue$, 2))\r
-\r
-POKE (externalAddress + address), secondByteValue\r
-POKE (externalAddress + address + 1), firstByteValue\r
-\r
-END SUB\r
-\r
-FUNCTION rn\r
-\r
-randomPointer = randomPointer + 1\r
-IF randomPointer > 10000 THEN randomPointer = 0\r
-rn = randomValue(randomPointer)\r
-\r
-END FUNCTION\r
-\r
-SUB rndInit\r
-\r
-FOR index = 0 TO 10000\r
- randomValue(index) = RND\r
-NEXT index\r
-\r
-randomPointer = 0\r
-END SUB\r
-\r
-SUB start\r
-\r
-starText\r
-\r
-SCREEN 7, , , 1\r
-\r
-maxMove = 50\r
-rndInit\r
-\r
-END SUB\r
-\r
-SUB starText\r
-\r
-DEF SEG = 0 ' read first from interrupt table\r
-\r
-externalSegment = PEEK(&H79 * 4 + 3) * 256\r
-externalSegment = externalSegment + PEEK(&H79 * 4 + 2)\r
-\r
-PRINT "Segment is: " + HEX$(externalSegment)\r
-\r
-externalAddress = PEEK(&H79 * 4 + 1) * 256\r
-externalAddress = externalAddress + PEEK(&H79 * 4 + 0)\r
-\r
-PRINT "relative address is:"; externalAddress\r
-\r
-DEF SEG = externalSegment\r
-\r
-IF getWord(0) <> 1983 THEN\r
- PRINT "FATAL ERROR: you must load"\r
- PRINT "QBasic extension TSR first!"\r
- SYSTEM\r
-END IF\r
-\r
-END SUB
\ No newline at end of file
+' Program to render a 3D point cloud galaxy.
+' By Svjatoslav Agejenko.
+' Email: svjatoslav@svjatoslav.eu
+' Homepage: http://www.svjatoslav.eu
+
+' Changelog:
+' 2003, Initial version.
+' 2024, Improved program readability using AI.
+
+' User can navigate through the galaxy using mouse or keyboard controls.
+' To use mouse controls, program requires a special TSR (terminate and stay resident)
+' program to be loaded. The TSR makes mouse input available to the QBasic program.
+
+' Navigation controls:
+' - Press left and right mouse buttons simultaneously to move in X and Z axis.
+' - Press right mouse button to move in Y axis.
+' - Use keyboard keys 'a', 'd', 'w', 's' to move in X and Z axis.
+' - Press 'q' to quit the program.
+
+DECLARE SUB temp ()
+DECLARE SUB mkGalaxy (x!, y!, z!)
+DECLARE SUB rndInit ()
+DECLARE FUNCTION rn! ()
+DECLARE SUB disp ()
+DECLARE SUB control ()
+DECLARE SUB putByte (addr!, dat!)
+DECLARE SUB putWord (addr!, dat!)
+DECLARE FUNCTION getWord! (addr!)
+DECLARE FUNCTION getByte! (addr!)
+DECLARE SUB start ()
+DECLARE SUB animate ()
+
+DIM SHARED angle1, angle2, angle3
+
+DIM SHARED time
+
+DIM SHARED externalSegment, externalAddress
+
+' Variables for the camera position and movement
+DIM SHARED myX, myY, myZ
+DIM SHARED speedX, speedY, speedZ
+DIM SHARED buttonLeft, buttonRight
+DIM SHARED maxMove
+
+' Variable for zoom level
+DIM SHARED zoom
+
+' Array to store random values
+DIM SHARED randomValue(0 TO 10000)
+DIM SHARED randomPointer
+
+' Arrays to store point coordinates and colors. These points make up galaxy.
+DIM SHARED pointX(1 TO 12000)
+DIM SHARED pointY(1 TO 12000)
+DIM SHARED pointZ(1 TO 12000)
+DIM SHARED pointColor(1 TO 12000)
+DIM SHARED numPoints
+
+' Temporary register array
+DIM SHARED temporaryRegister(0 TO 10)
+
+newLine = 0
+newPage = 0
+
+start
+
+currentX = 0
+currentY = 0
+currentZ = 0
+
+numPoints = 0
+mkGalaxy 0, 0, 0
+1
+
+' Generate a random angle for initial camera position
+variableAngle = INT(RND * 3)
+
+SELECT CASE variableAngle
+CASE 0
+ currentX = RND * 500 - 250
+CASE 1
+ currentY = RND * 100 - 50
+CASE 2
+ currentZ = RND * 500 - 250
+END SELECT
+
+control
+disp
+
+PCOPY 0, 1
+CLS
+GOTO 1
+
+SUB control
+
+' Check for mouse input
+IF getByte(8) <> 0 THEN
+ putByte 8, 0
+ xPosition = getWord(2)
+ putWord 2, 0
+ yPosition = getWord(4)
+ putWord 4, 0
+ button = getWord(6)
+ putWord 6, 0
+
+ ' Reset mouse buttons
+ buttonLeft = 0
+ buttonRight = 0
+
+ ' Handle mouse button states
+ IF button = 1 THEN buttonLeft = 1
+ IF button = 2 THEN buttonRight = 1
+ IF button = 3 THEN buttonLeft = 1: buttonRight = 1
+
+ ' Handle mouse movements
+ IF buttonRight = 1 THEN
+ IF buttonLeft = 1 THEN
+ speedX = speedX + SIN(angle1) * yPosition / 4
+ speedZ = speedZ - COS(angle1) * yPosition / 4
+ GOTO 3
+ END IF
+ speedY = speedY + yPosition / 4
+ 3
+ yPosition = 0
+ END IF
+
+END IF
+
+' Limit the position values to prevent overflow
+IF xPosition < -maxMove THEN xPosition = -maxMove
+IF xPosition > maxMove THEN xPosition = maxMove
+angle1 = angle1 - xPosition / 150
+
+IF yPosition < -maxMove THEN yPosition = -maxMove
+IF yPosition > maxMove THEN yPosition = maxMove
+angle2 = angle2 - yPosition / 150
+
+' Check for keyboard input
+keyInput$ = INKEY$
+
+' Handle keyboard controls
+IF keyInput$ = "a" THEN speedX = speedX - COS(angle1): speedZ = speedZ - SIN(angle1)
+IF keyInput$ = "d" THEN speedX = speedX + COS(angle1): speedZ = speedZ + SIN(angle1)
+IF keyInput$ = "w" THEN speedX = speedX - SIN(angle1): speedZ = speedZ + COS(angle1)
+IF keyInput$ = "s" THEN speedX = speedX + SIN(angle1): speedZ = speedZ - COS(angle1)
+IF keyInput$ = "q" THEN SYSTEM
+
+' Decelerate the movement
+speedX = speedX / 1.1
+speedY = speedY / 1.1
+speedZ = speedZ / 1.1
+
+myX = myX + speedX
+myZ = myZ + speedZ
+myY = myY + speedY
+
+END SUB
+
+SUB disp
+
+' Calculate sine and cosine values for rotation angles
+sinAngle1 = SIN(angle1)
+cosAngle1 = COS(angle1)
+sinAngle2 = SIN(angle2)
+cosAngle2 = COS(angle2)
+
+' Loop through all points to calculate and display their positions
+FOR pointIndex = 1 TO numPoints
+
+ ' Calculate the distance from the camera
+ xCoordinate = pointX(pointIndex) - myX
+ yCoordinate = pointY(pointIndex) - myY
+ zCoordinate = pointZ(pointIndex) - myZ
+
+ ' Rotate the points
+ rotatedX = xCoordinate * cosAngle1 + zCoordinate * sinAngle1
+ rotatedZ = zCoordinate * cosAngle1 - xCoordinate * sinAngle1
+
+ rotatedY = yCoordinate * cosAngle2 + rotatedZ * sinAngle2
+ finalZ = rotatedZ * cosAngle2 - yCoordinate * sinAngle2
+
+ ' Draw points that are sufficiently close
+ IF finalZ > 3 THEN
+ screenX = rotatedX / finalZ * 130 + 160
+ screenY = rotatedY / finalZ * 130 + 100
+ PSET (screenX, screenY), pointColor(pointIndex)
+ END IF
+
+NEXT pointIndex
+END SUB
+
+FUNCTION getByte (address)
+getByte = PEEK(externalAddress + address)
+END FUNCTION
+
+FUNCTION getWord (address)
+firstByte = PEEK(externalAddress + address)
+secondByte = PEEK(externalAddress + address + 1)
+
+' Combine the two bytes into a single value
+combinedHex$ = HEX$(firstByte)
+IF LEN(combinedHex$) = 1 THEN combinedHex$ = "0" + combinedHex$
+IF LEN(combinedHex$) = 0 THEN combinedHex$ = "00"
+
+combinedValue = VAL("&H" + HEX$(secondByte) + combinedHex$)
+
+getWord = combinedValue
+END FUNCTION
+
+SUB mkGalaxy (localX, localY, localZ)
+
+' Generate random angles for stars within galaxy
+randomAngle1 = rn * 10
+randomAngle2 = rn * 10
+
+galaxySin1 = SIN(randomAngle1)
+galaxyCos1 = COS(randomAngle1)
+galaxySin2 = SIN(randomAngle2)
+galaxyCos2 = COS(randomAngle2)
+
+randomPointer = 0
+size = 100
+piValue = 3.14
+spiralBarsMultiplier = 3
+
+FOR pointIndex = 1 TO 10000
+
+ ' Generate a random star distance from the center
+ randomVariable = rn * 10
+ distanceFromCenter = randomVariable * randomVariable / 30
+
+ ' Calculate spiral offset and half of it
+ spiralOffset = rn * (11.5 - randomVariable) / 3
+ halfSpiralOffset = spiralOffset / 2
+
+ ' Generate angle exponent and spiral bar angle
+ angleExponent = rn * (distanceFromCenter / 2) / spiralBarsMultiplier * 2
+ spiralBarAngle = 2 * piValue / spiralBarsMultiplier * INT(rn * spiralBarsMultiplier)
+
+ ' Calculate x, z, and y coordinates
+ xCoordinate = (SIN(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size
+ zCoordinate = (COS(randomVariable - spiralBarAngle + angleExponent) * distanceFromCenter + rn * spiralOffset - halfSpiralOffset) * size
+ yCoordinate = (rn * spiralOffset - halfSpiralOffset) * size
+
+ ' Rotate the coordinates
+ rotatedX = xCoordinate * galaxyCos1 + zCoordinate * galaxySin1
+ rotatedZ = zCoordinate * galaxyCos1 - xCoordinate * galaxySin1
+
+ rotatedY = yCoordinate * galaxyCos2 + rotatedZ * galaxySin2
+ finalZ = rotatedZ * galaxyCos2 - yCoordinate * galaxySin2
+
+ ' Store the point in arrays
+ numPoints = numPoints + 1
+
+ pointX(numPoints) = rotatedX + localX
+ pointY(numPoints) = rotatedY + localY
+ pointZ(numPoints) = finalZ + localZ
+ pointColor(numPoints) = INT(RND * 15) + 1
+NEXT pointIndex
+
+END SUB
+
+SUB putByte (address, dataByte)
+
+POKE (externalAddress + address), dataByte
+END SUB
+
+SUB putWord (address, dataWord)
+
+' Convert the word to a hexadecimal string
+hexValue$ = HEX$(dataWord)
+
+' Ensure the string has at least 4 characters
+2
+IF LEN(hexValue$) < 4 THEN
+ hexValue$ = "0" + hexValue$: GOTO 2
+END IF
+
+' Extract the first and second bytes
+firstByteValue = VAL("&H" + LEFT$(hexValue$, 2))
+secondByteValue = VAL("&H" + RIGHT$(hexValue$, 2))
+
+' Store the bytes in memory
+POKE (externalAddress + address), secondByteValue
+POKE (externalAddress + address + 1), firstByteValue
+
+END SUB
+
+FUNCTION rn
+
+' Increment the random pointer and get a new random value
+randomPointer = randomPointer + 1
+IF randomPointer > 10000 THEN randomPointer = 0
+rn = randomValue(randomPointer)
+
+END FUNCTION
+
+SUB rndInit
+
+' Initialize the array of random values
+FOR index = 0 TO 10000
+ randomValue(index) = RND
+NEXT index
+
+randomPointer = 0
+END SUB
+
+SUB start
+
+starText
+
+' Set up graphics mode
+SCREEN 7, , , 1
+
+' Initialize maximum movement and random values
+maxMove = 50
+rndInit
+
+END SUB
+
+SUB starText
+
+' Read the segment and address from the interrupt table
+DEF SEG = 0 ' read first from interrupt table
+
+externalSegment = PEEK(&H79 * 4 + 3) * 256
+externalSegment = externalSegment + PEEK(&H79 * 4 + 2)
+
+PRINT "Segment is: " + HEX$(externalSegment)
+
+externalAddress = PEEK(&H79 * 4 + 1) * 256
+externalAddress = externalAddress + PEEK(&H79 * 4 + 0)
+
+PRINT "relative address is:"; externalAddress
+
+' Read the word at the specified address
+DEF SEG = externalSegment
+
+IF getWord(0) <> 1983 THEN
+ PRINT "FATAL ERROR: you must load"
+ PRINT "QBasic extension TSR first!"
+ SYSTEM
+END IF
+
+END SUB