09-05-2025, 09:53 PM
(This post was last modified: 09-05-2025, 10:03 PM by madscijr.
Edit Reason: replaced export as forum codebox (was displaying weird and slowing down the web page) and attached images (doh)
)
Trying to do collision detection. Version 1 function ShapesOverlap% works.
I asked google for help with speeding it up with _MEM functions,
and it spat out ShapesOverlapMem%, but when I try running it,
I'm getting a "Memory region out of range" error.
I'm not really a _MEM expert, I don't even play one on TV.
If anyone would look at this and give me a clue that would be much appreciated...
![[Image: shape1.png]](https://i.ibb.co/B2ZLmN9S/shape1.png)
![[Image: shape2.png]](https://i.ibb.co/gMKbVjvW/shape2.png)
![[Image: shape3.png]](https://i.ibb.co/fG8RJpfj/shape3.png)
![[Image: shape4.png]](https://i.ibb.co/zWRcTWLH/shape4.png)
I asked google for help with speeding it up with _MEM functions,
and it spat out ShapesOverlapMem%, but when I try running it,
I'm getting a "Memory region out of range" error.
I'm not really a _MEM expert, I don't even play one on TV.
If anyone would look at this and give me a clue that would be much appreciated...
Code: (Select All)
' *****************************************************************************
' *****************************************************************************
' Function ShapesOverlapMem% FAILS WITH Memory region out of range ERROR at
' Line 323: _MemGet mem1, offset1, color1
'
' Function ShapesOverlap% works.
' *****************************************************************************
' *****************************************************************************
' Q:
' How might we leverage _MEM, _MEMIMAGE and similar functions to speed up the
' image overlap test?
' A:
' That's an excellent question. Leveraging direct memory access with _MEM and
' _MEMIMAGE is a classic and powerful optimization technique in QB64, especially
' for pixel-level operations.
'
' The current ShapesOverlap% function is slowed down by the repeated calls to
' Point(x, y). Each call has a certain amount of overhead. By accessing the image
' data directly in memory, we can bypass this overhead and read the pixel data
' much faster.
'
' The Strategy: From Point to Pointers
' Get Memory Access: For each of the two shape images, we'll use _MEMIMAGE to
' create a _MEM block. This block acts as a direct pointer to the raw pixel data
' of the image.
'
' Calculate Offsets: Instead of asking for the color at coordinate (x, y), we'll
' calculate the exact memory address (or offset) of that pixel's data within the
' _MEM block. For a 32-bit image, each pixel takes 4 bytes, so the offset for a
' pixel at (px, py) in an image of width is (py * width + px) * 4.
'
' Read Data Directly: We'll use _MEMGET to read the 4-byte color value directly
' from that calculated offset.
'
' Release Memory: After we're done, we must use _MEMFREE to release the _MEM
' blocks to prevent memory leaks.
'
' This approach significantly reduces the function call overhead inside the
' loops, resulting in a much faster overlap test, especially for larger shapes or
' more complex scenes.
'
' I have updated the ShapesOverlap% function in your file as ShapesOverlapMem%
' to use this memory-based approach. The core logic of finding the intersection
' area remains the same, but the pixel checking part is now much more efficient.
'
' Here is the updated file:
_Title "Alpha Overlap Demo"
Type ShapeType
height As Long
width As Long
color As _Unsigned Long
image As Long ' image handle (image handles are long not _Unsigned Long)
x As Long
y As Long
End Type ' ShapeType
Dim MyScreen As Long
Dim arrImg(1 To 4) As ShapeType
Dim i1 As Integer
Dim i2 As Integer
Dim bOverlaps As Integer
Dim RowNum As Integer
Dim bCompareBothWays As Integer
Dim bUseMemFunctions As Integer
Dim in$
' MAIN LOOP
Do
Screen 0
Cls
Print "Collision detection"
Print "-------------------"
Print
' Prompt user for preferences
Do
Print "ELIMINATE DUPLICATES?"
Print " y = to compare both A:B and B:A"
Print " n = to eliminate duplicates"
Print " q = quit"
Input "Compare both ways (y/n/q)"; in$
in$ = LCase$(_Trim$(in$))
If in$ = "y" Then
bCompareBothWays = _TRUE: Exit Do
ElseIf in$ = "n" Then
bCompareBothWays = _FALSE: Exit Do
ElseIf in$ = "q" Then
GoTo CleanupAndExit
Else
Print "Please type y or n."
End If
_KeyClear: _Delay .5 ' CLEAR KEYBOARD BUFFER
Loop
Print
Do
Print "TRY FAST _MEM METHOD?"
Print " y = _MEM method (experimental)"
Print " n = proven but slower method"
Print " q = quit"
Input "Use fast _MEM method (y/n/q)"; in$
in$ = LCase$(_Trim$(in$))
If in$ = "y" Then
bUseMemFunctions = _TRUE: Exit Do
ElseIf in$ = "n" Then
bUseMemFunctions = _FALSE: Exit Do
ElseIf in$ = "q" Then
GoTo CleanupAndExit
Else
Print "Please type y or n."
End If
_KeyClear: _Delay .5 ' CLEAR KEYBOARD BUFFER
Loop
' Init screen
MyScreen = _NewImage(640, 480, 32): Screen MyScreen: Cls , _RGB32(0, 0, 0)
' Create 4 images in memory
' Create a red square with some transparency
arrImg(1).height = 60
arrImg(1).width = 60
arrImg(1).color = _RGBA32(255, 0, 0, 150)
arrImg(1).image = _NewImage(arrImg(1).width, arrImg(1).height, 32)
_Dest arrImg(1).image: Cls , arrImg(1).color
arrImg(1).x = 10
arrImg(1).y = 10
' Create a green rectangle with some transparency
arrImg(2).height = 30
arrImg(2).width = 90
arrImg(2).color = _RGBA32(0, 255, 0, 150)
arrImg(2).image = _NewImage(arrImg(2).width, arrImg(2).height, 32)
_Dest arrImg(2).image: Cls , arrImg(2).color
arrImg(2).x = 80
arrImg(2).y = 40
' Create a blue ellipse with some transparency
arrImg(3).height = 20
arrImg(3).width = 30
arrImg(3).color = _RGBA32(0, 0, 255, 150)
arrImg(3).image = _NewImage(arrImg(3).width, arrImg(3).height, 32)
_Dest arrImg(3).image: Cls , _RGBA32(0, 0, 0, 0)
EllipseFill arrImg(3).width \ 2, arrImg(3).height \ 2, arrImg(3).width \ 2, arrImg(3).height \ 2, arrImg(3).color
arrImg(3).x = 60
arrImg(3).y = 60
' Create a yellow circle with no transparency
arrImg(4).height = 45
arrImg(4).width = 45
arrImg(4).color = _RGB32(255, 255, 0)
arrImg(4).image = _NewImage(arrImg(4).width, arrImg(4).height, 32)
_Dest arrImg(4).image: Cls , _RGBA32(0, 0, 0, 0)
CircleFill arrImg(4).width \ 2, arrImg(4).height \ 2, arrImg(4).width \ 2, arrImg(4).color
arrImg(4).x = 80
arrImg(4).y = 10
' Display the images on the screen
_Dest MyScreen ' Set destination back to the main screen
For i1 = LBound(arrImg) To UBound(arrImg)
_PutImage (arrImg(i1).x, arrImg(i1).y), arrImg(i1).image
Next i1
' -----------------------------------------------------------------------------
' Check for overlap
' -----------------------------------------------------------------------------
If bCompareBothWays Then
' VERSION #1 LETS US TEST BOTH COMPARE(A,B) AND COMPARE(B,A)
RowNum = 10
For i1 = LBound(arrImg) To UBound(arrImg)
For i2 = LBound(arrImg) To UBound(arrImg)
If i2 <> i1 Then
' USE MEM VERSION OR REGULAR VERSION?
If bUseMemFunctions Then
bOverlaps = ShapesOverlapMem%(arrImg(i1), arrImg(i2))
Else
bOverlaps = ShapesOverlap%(arrImg(i1), arrImg(i2))
End If
' Print next result
_Dest MyScreen
RowNum = RowNum + 1: Locate RowNum, 1
If bOverlaps = _TRUE Then
Color _RGB32(255, 0, 0), _RGB32(0, 0, 0): Print "* ";
Color _RGB32(0, 0, 0), arrImg(i1).color: Print "Shape #" + _ToStr$(i1);
Color _RGB32(255, 0, 0), _RGB32(0, 0, 0): Print " and ";
Color _RGB32(0, 0, 0), arrImg(i2).color: Print "Shape #" + _ToStr$(i2);
Color _RGB32(255, 0, 0), _RGB32(0, 0, 0): Print " overlap.";
Else
Color _RGB32(0, 255, 255), _RGB32(0, 0, 0): Print " ";
Color _RGB32(0, 0, 0), arrImg(i1).color: Print "Shape #" + _ToStr$(i1);
Color _RGB32(0, 255, 255), _RGB32(0, 0, 0): Print " and ";
Color _RGB32(0, 0, 0), arrImg(i2).color: Print "Shape #" + _ToStr$(i2);
Color _RGB32(0, 255, 255), _RGB32(0, 0, 0): Print " do not overlap."
End If
End If
Next i2
Next i1
Else
' VERSION #2 TOTALLY ELIMINATES DUPLICATES
RowNum = 10
For i1 = LBound(arrImg) To UBound(arrImg)
For i2 = i1 + 1 To UBound(arrImg) ' Check each pair only once
' USE MEM VERSION OR REGULAR VERSION?
If bUseMemFunctions Then
bOverlaps = ShapesOverlapMem%(arrImg(i1), arrImg(i2))
Else
bOverlaps = ShapesOverlap%(arrImg(i1), arrImg(i2))
End If
' Print next result for (i1, i2)
_Dest MyScreen
RowNum = RowNum + 1: Locate RowNum, 1
If bOverlaps Then
Color _RGB32(255, 0, 0), _RGB32(0, 0, 0): Print "* ";
Color _RGB32(0, 0, 0), arrImg(i1).color: Print "Shape #" + _ToStr$(i1);
Color _RGB32(255, 0, 0), _RGB32(0, 0, 0): Print " and ";
Color _RGB32(0, 0, 0), arrImg(i2).color: Print "Shape #" + _ToStr$(i2);
Color _RGB32(255, 0, 0), _RGB32(0, 0, 0): Print " overlap.";
Else
Color _RGB32(0, 255, 255), _RGB32(0, 0, 0): Print " ";
Color _RGB32(0, 0, 0), arrImg(i1).color: Print "Shape #" + _ToStr$(i1);
Color _RGB32(0, 255, 255), _RGB32(0, 0, 0): Print " and ";
Color _RGB32(0, 0, 0), arrImg(i2).color: Print "Shape #" + _ToStr$(i2);
Color _RGB32(0, 255, 255), _RGB32(0, 0, 0): Print " do not overlap."
End If
Next i2
Next i1
End If
' DONE
RowNum = RowNum + 2: Locate RowNum, 1
Color _RGB32(160, 160, 160): Print "Press any key to continue"
_Delay .5: Sleep: _KeyClear: _Delay .5 ' CLEAR KEYBOARD BUFFER
Screen 0
For i1 = LBound(arrImg) To UBound(arrImg): FreeImage arrImg(i1).image: Next i1
FreeImage MyScreen
Loop
CleanupAndExit:
Screen 0
For i1 = LBound(arrImg) To UBound(arrImg): FreeImage arrImg(i1).image: Next i1
FreeImage MyScreen
System
' /////////////////////////////////////////////////////////////////////////////
' FUNCTION: ShapesOverlapMem%
'
' Compares two shapes to determine if any of their non-transparent pixels overlap.
' This version uses _MEMIMAGE for fast, direct memory access to pixel data.
'
' PARAMETERS:
' shape1: The first shape to compare.
' shape2: The second shape to compare.
'
' RETURNS:
' _TRUE if an alpha overlap is found, _FALSE otherwise.
Function ShapesOverlapMem% (shape1 As ShapeType, shape2 As ShapeType)
Dim bHasOverlap As Integer
Dim x As Long, y As Long ' Loop iterators for absolute screen coordinates
Dim x1 As Long, y1 As Long ' Relative coordinates for shape1's image
Dim x2 As Long, y2 As Long ' Relative coordinates for shape2's image
Dim color1 As _Unsigned Long
Dim color2 As _Unsigned Long
Dim overlapX1 As Long, overlapY1 As Long
Dim overlapX2 As Long, overlapY2 As Long
' === New variables for memory access ===
Dim mem1 As _MEM, mem2 As _MEM
Dim offset1 As _Offset, offset2 As _Offset
' Initialize
bHasOverlap = _FALSE
' Define bounding boxes for each shape based on their screen position and dimensions
Dim box1X1 As Long: box1X1 = shape1.x
Dim box1Y1 As Long: box1Y1 = shape1.y
Dim box1X2 As Long: box1X2 = shape1.x + shape1.width - 1
Dim box1Y2 As Long: box1Y2 = shape1.y + shape1.height - 1
Dim box2X1 As Long: box2X1 = shape2.x
Dim box2Y1 As Long: box2Y1 = shape2.y
Dim box2X2 As Long: box2X2 = shape2.x + shape2.width - 1
Dim box2Y2 As Long: box2Y2 = shape2.y + shape2.height - 1
' Find the intersection of the two bounding boxes.
If box1X1 > box2X1 Then overlapX1 = box1X1 Else overlapX1 = box2X1
If box1Y1 > box2Y1 Then overlapY1 = box1Y1 Else overlapY1 = box2Y1
If box1X2 < box2X2 Then overlapX2 = box1X2 Else overlapX2 = box2X2
If box1Y2 < box2Y2 Then overlapY2 = box1Y2 Else overlapY2 = box2Y2
' If the intersection area is valid, check each pixel within it.
If overlapX1 <= overlapX2 And overlapY1 <= overlapY2 Then
' === Get direct memory access to the images ===
mem1 = _MemImage(shape1.image)
mem2 = _MemImage(shape2.image)
For y = overlapY1 To overlapY2
For x = overlapX1 To overlapX2
' Calculate coordinates relative to each shape's own image canvas
x1 = x - shape1.x
y1 = y - shape1.y
x2 = x - shape2.x
y2 = y - shape2.y
' === Read pixel data directly from memory ===
' Calculate byte offset: (y * width + x) * 4 bytes per pixel
offset1 = (y1 * shape1.width + x1) * 4
offset2 = (y2 * shape2.width + x2) * 4
' *****************************************************************************
' *****************************************************************************
' PROGRAM CRASHES AT _MemGet WITH Memory region out of range ERROR
' *****************************************************************************
' *****************************************************************************
_MemGet mem1, offset1, color1
_MemGet mem2, offset2, color2
' Check if both pixels are non-transparent (alpha > 0)
If _Alpha(color1) > 0 And _Alpha(color2) > 0 Then
bHasOverlap = _TRUE
Exit For
End If
Next x
If bHasOverlap = _TRUE Then Exit For
Next y
' === IMPORTANT: Free the memory blocks when done ===
_MemFree mem1
_MemFree mem2
End If
' Return the result
ShapesOverlapMem% = bHasOverlap
End Function ' ShapesOverlapMem%
' /////////////////////////////////////////////////////////////////////////////
' FUNCTION: ShapesOverlap%
'
' Compares two shapes to determine if any of their non-transparent pixels overlap.
'
' PARAMETERS:
' shape1: The first shape to compare.
' shape2: The second shape to compare.
'
' RETURNS:
' _TRUE if an alpha overlap is found, _FALSE otherwise.
Function ShapesOverlap% (shape1 As ShapeType, shape2 As ShapeType)
Dim bHasOverlap As Integer
Dim x As Long, y As Long ' Loop iterators for absolute screen coordinates
Dim x1 As Long, y1 As Long ' Relative coordinates for shape1's image
Dim x2 As Long, y2 As Long ' Relative coordinates for shape2's image
Dim color1 As _Unsigned Long
Dim color2 As _Unsigned Long
Dim overlapX1 As Long, overlapY1 As Long
Dim overlapX2 As Long, overlapY2 As Long
' Initialize
bHasOverlap = _FALSE
' Define bounding boxes for each shape based on their screen position and dimensions
Dim box1X1 As Long: box1X1 = shape1.x
Dim box1Y1 As Long: box1Y1 = shape1.y
Dim box1X2 As Long: box1X2 = shape1.x + shape1.width - 1
Dim box1Y2 As Long: box1Y2 = shape1.y + shape1.height - 1
Dim box2X1 As Long: box2X1 = shape2.x
Dim box2Y1 As Long: box2Y1 = shape2.y
Dim box2X2 As Long: box2X2 = shape2.x + shape2.width - 1
Dim box2Y2 As Long: box2Y2 = shape2.y + shape2.height - 1
' Find the intersection of the two bounding boxes.
' This is the only area where pixels could possibly overlap.
If box1X1 > box2X1 Then overlapX1 = box1X1 Else overlapX1 = box2X1
If box1Y1 > box2Y1 Then overlapY1 = box1Y1 Else overlapY1 = box2Y1
If box1X2 < box2X2 Then overlapX2 = box1X2 Else overlapX2 = box2X2
If box1Y2 < box2Y2 Then overlapY2 = box1Y2 Else overlapY2 = box2Y2
' If the intersection area is valid, check each pixel within it.
If overlapX1 <= overlapX2 And overlapY1 <= overlapY2 Then
For y = overlapY1 To overlapY2
For x = overlapX1 To overlapX2
' Calculate coordinates relative to each shape's own image canvas
x1 = x - shape1.x
y1 = y - shape1.y
x2 = x - shape2.x
y2 = y - shape2.y
' Get the color of the pixel from each source image
_Source shape1.image: color1 = Point(x1, y1)
_Source shape2.image: color2 = Point(x2, y2)
' Check if both pixels are non-transparent (alpha > 0)
If _Alpha(color1) > 0 And _Alpha(color2) > 0 Then
' Found an overlap, set flag and exit loops immediately
bHasOverlap = _TRUE
Exit For
End If
Next x
If bHasOverlap = _TRUE Then Exit For
Next y
End If
' Return the result
ShapesOverlap% = bHasOverlap
End Function ' ShapesOverlap%
' /////////////////////////////////////////////////////////////////////////////
Sub FreeImage (ThisImage&)
If ThisImage& < -1 Or ThisImage& > 0 Then _FreeImage ThisImage&
End Sub ' FreeImage
' /////////////////////////////////////////////////////////////////////////////
Sub CircleFill (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
' CX = center x coordinate
' CY = center y coordinate
' R = radius
' C = fill color
Dim Radius As Integer, RadiusError As Integer
Dim X As Integer, Y As Integer
Radius = Abs(R)
RadiusError = -Radius
X = Radius
Y = 0
If Radius = 0 Then PSet (CX, CY), C: Exit Sub
Line (CX - X, CY)-(CX + X, CY), C, BF
While X > Y
RadiusError = RadiusError + Y * 2 + 1
If RadiusError >= 0 Then
If X <> Y + 1 Then
Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
End If
X = X - 1
RadiusError = RadiusError - X * 2
End If
Y = Y + 1
Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
Wend
End Sub ' CircleFill
' /////////////////////////////////////////////////////////////////////////////
Sub EllipseFill (CX As Integer, CY As Integer, a As Integer, b As Integer, C As _Unsigned Long)
' CX = center x coordinate
' CY = center y coordinate
' a = semimajor axis
' b = semiminor axis
' C = fill color
If a = 0 Or b = 0 Then Exit Sub
Dim h2 As _Integer64
Dim w2 As _Integer64
Dim h2w2 As _Integer64
Dim x As Integer
Dim y As Integer
w2 = a * a
h2 = b * b
h2w2 = h2 * w2
Line (CX - a, CY)-(CX + a, CY), C, BF
Do While y < b
y = y + 1
x = Sqr((h2w2 - y * y * w2) \ h2)
Line (CX - x, CY + y)-(CX + x, CY + y), C, BF
Line (CX - x, CY - y)-(CX + x, CY - y), C, BF
Loop
End Sub ' EllipseFill
' /////////////////////////////////////////////////////////////////////////////
'DrawRectSolid x%, y%, width%, height%, fgColor~&
Sub DrawRectSolid (iX As Integer, iY As Integer, iSizeW As Integer, iSizeH As Integer, fgColor As _Unsigned Long)
Line (iX, iY)-(iX + (iSizeW - 1), iY + (iSizeH - 1)), fgColor, BF ' Draw a solid rectangle
End Sub ' DrawRectSolid![[Image: shape1.png]](https://i.ibb.co/B2ZLmN9S/shape1.png)
![[Image: shape2.png]](https://i.ibb.co/gMKbVjvW/shape2.png)
![[Image: shape3.png]](https://i.ibb.co/fG8RJpfj/shape3.png)
![[Image: shape4.png]](https://i.ibb.co/zWRcTWLH/shape4.png)



