View Full Version : Array Scans UDTs?
ReneMiner
05-04-2013, 13:18
My question of today is about Array Scan
I want to know if I can use this to scan my UDT-Arrays somehow, I imagine it's not possible using the usual way because of more than one Element to scan.
So I have some UDT and some global array of it like this:
Type t_myType
X as Double
Y as Double
Z as Double
End Type
Dim foo() as t_myType : Redim foo(1)
Dim nFoos as Long ' count of current used foo()'s
' I use this counter anyway because Ubound in my script is mostly higher than
' the actual count of elements, so I don't ReDim too often...
now I could go there and scan my foo()-array for an element that has .X = 1, .Y =2 and .Z = 3 and I call this to a function that shall return which foo() has these values. So this the function raw skeleton:
EDIT:
--removed--
Sometimes it helps to "think out loud" in forum :D
It works that way:
Function Get_Foo_ID(ByVal X As Double, Y As Double, Z As Double) As Long
Static sXYZ As String
If nFoos > 0 Then
sXYZ = MKD$(X) + MKD$(Y) + MKD$(Z)
Local sFoos(nFoos) As String * SizeOf(t_myType) At VarPtr(foo(1))
Function = Array Scan sFoos For nFoos, = sXYZ
endif
End Function
Edit again:
Now I experience something strange - I use this in four of my functions and they work a few times before crashing.
I guess it crashes when Array Scan finds a match.
I already tried to ReDim the virtual variable at 0 before exiting function but still keeps crashing. Probably its because the strings are fixed-size length and "Array Scan" does not like these...
Is there some other fast way to scan the array without looping through all elements and comparing each one by one? Currently I Peek$ from foo(1) and go using Instr. Before I used For-Next-Loop to cycle through all elements and compare X,Y and Z but that seems to be very slow and old fashioned method. Is Instr slow method too? Any better suggest to compare thousands of bytes and find the matching?
ErosOlmi
05-04-2013, 21:48
Rene,
Array Scan ... only works on dynamic string and numeric arrays.
Fixed strings are not allowed at the moment. See Restrictions in Array Scan help: http://www.thinbasic.com/public/products/thinBasic/help/html/index.html?array_scan.htm
I will see if I can add fixed strings arrays because your method in Get_Foo_ID function is very interesting.
Maybe this example can give you some help: http://www.thinbasic.com/community/showthread.php?11079-clicking-on-Geometry-Figures&p=82846&viewfull=1#post82846
It checks if mouse is inside a figure area.
It all depends on the number of elements into your array. If hundred or thousands.
Ciao
Eros
ReneMiner
06-04-2013, 09:19
yes I checked example, it's using For-Next-loop to cycle through the elements. I had for-next inside my functions too but i got the feeling that array-scan would be much faster method to localize some element - since all elements have to be unique. This was the function I used before:
Function VecID(ByVal X As Double, Y As Double, Z As Double, _
Optional TestExist As Boolean) As Long
' returns index of existing vector or creates it
' if TestExist then returns 0 if not found
Static lCount As Long
If nVecs > 0 Then
For lCount = 1 to nVexs
If Vec(lCount).X = X And Vec(lCount).Y = Y And Vec(lCount).Z = Z then Return lCount
Next lCount ' << I know you don't like this thinBasic knows... but I forget sometimes
EndIf
If TestExist Then Return 0
' need a new one...
If nVecs = 0 Or nVecs >= UBound(Vec) Then ReDim Preserve Vec(nVecs + 1000)
nVecs += 1
Vec(nVecs).X = X
Vec(nVecs).Y = Y
Vec(nVecs).Z = Z
Function = nVecs
End Function
this how it is now, seems to be faster if more than 20000 Elements but what I don't like about - it limits the capacity to MaxLenString/(3*SizeOf(Double)) and I have to recheck if on correct position inside the string.
Function VecID(ByVal X As Double, Y As Double, Z As Double, _
Optional TestExist As Boolean) As Long
' returns index of existing vector or creates it
' if TestExist then returns 0 if not found
Static lCount As Long
Static sXYZ, sVecs As String
If nVecs > 0 Then
sXYZ = MKD$(X) + MKD$(Y) + MKD$(Z)
sVecs = Peek$(VarPtr(Vec(1)), nVecs * SizeOf(t_Vec3d))
lCount = -1
Do
lCount = InStr(lCount + 1, sVecs, sXYZ)
Loop Until lCount = 0 Or Mod(lCount - 1, SizeOf(t_vec3d)) = 0
sVecs = ""
If lCount Then Return (lCount -1)/SizeOf(t_vec3d) + 1
EndIf
If TestExist Then Return 0
' need a new one...
' ...skip here
End Function
And the way I would like to have it: See first post. I think Scan Array is the fastest that thinBasic offers. Or maybe you got some hint for me- perhaps my mind is too fixed to strings already: if it was not 3 Doubles but 2 longs I could make quad variabletype from it and scan the virtual array for numerals. But in fact those vertices and vectors and colors i use, always have 3 components, except the uv which has 2 Doubles. Would be helpful already if I could do it the Scan-Way just for uv but theres no data-type that offers 16 Bytes in one single variable.
Is there perhaps some way using UNION to make a numeral-user-type out of 2 or 3 numerals that Array-Scan could accept? Would EVAL-Module offer some solution? Or could I set up just one UDT-Element which to compare and pass the UDT-Size to Array-Scan as this:
' pseudo-code of course
... type as above in foo()-example
local toFind as t_myType
toFind.X = 1
toFind.Y = 2
toFind.Z = 3
Function = Array Scan foo for nFoos, = toFind [,SizeOf(t_myType)]
'...
ErosOlmi
06-04-2013, 11:49
Hi Renč,
Getting you original idea .... I think I've developed something interesting and quite fast.
Below an example (taking your original example) using 100000 elements and finding element stored at position 50000 takes on my computer 0,013 seconds.
In future it can be improved but for now it seems fine to me.
Type t_myType
'---Just added some dummy bytes in order to demonstrate below array scan new BYTE option
aa As String * 10
'---This part will be the one we will search for.
X As Double
Y As Double
Z As Double
'---Here some other bytes
a As Long
b As Long
s As String * 100
'....
End Type
Dim nFoos As Long = 100000
Dim foo() As t_myType
ReDim foo(nFoos)
foo(50000).x = 1
foo(50000).y = 1
foo(50000).z = 1
foo(50000).s = "abc"
Dim lPos As Long
Dim T1, T2 As Double
T1 = Timer
lPos = Get_Foo_ID(1, 1, 1)
t2 = Timer
If lPos Then
MsgBox 0, "Element found at position " & lPos & $CRLF & "String is: " & foo(lPos).s & $CRLF & "Time taken (secs):" & Format$(T2 - T1, "#0.000")
Else
MsgBox 0, "Element not found" & $CRLF & "Time taken (secs):" & Format$(T2 - T1, "#0.000")
End If
'-------------------------------------------------------------------------
Function Get_Foo_ID(ByVal X As Double, Y As Double, Z As Double) As Long
'-------------------------------------------------------------------------
Static ToFind As t_myType
'---We want to find element having the following data
ToFind.x = x
ToFind.y = y
ToFind.z = z
If nFoos > 0 Then
'---Because array scan now works with fixed strings ...
'---Create a virtual array of fixed strings over our array of UDTs
Local sFoos(nFoos) As String * SizeOf(t_myType) At VarPtr(foo(1))
'---Just search from byte 11 for SizeOf(Double) * 3 bytes the equivalent byts in ToFind UDT
Function = Array Scan sFoos, Byte(11, SizeOf(Double) * 3), = ToFind
EndIf
End Function
Mainly I have modified Array Scan to work with fixed strings and accepting another syntax: BYTE(Start, Len) in order to be able to scan fixed string buffer for a string starting at byte and for X bytes len.
In the above example it scans sFoos virtual variable overimposed to array of UDT and scanning from byte 1 for the len of the 3 doubles for a string that is in reality a temp UDT
When scanning it takes only from Byte start for BYTE Len bytes both from array and from comparison string in order to check.
So new ARRAY SCAN Syntax is:
ARRAY SCAN ArrayVariable([StartIndex]) [FOR nElements] [, COLLATE UCASE], Expression
or
ARRAY SCAN ArrayVariable([StartIndex]) [FOR nElements] [, BYTE(Start [, Length]) ], = StringExpression
Let me know.
Eros
Attached to this post: thinCore.DLL to be placed into \thinBasic\ directory substituting the one already present.
But ATTENTION: only if you have current thinBasic beta 1.9.4 installed.
Attached file last update on 2013/04/07 at 20:10 CET: added UDT_ElementByte, changed UDT_ElementOffSet
Attached file last update on 2013/04/07 at 15:53 CET: added multiple strings/UDT for ARRAY SCAN ... comparisons
Attached file last update on 2013/04/07 at 14:27 CET: fixed UDT_ElementOffSet GPF
Attached file last update on 2013/04/07 at 12:48 CET
ReneMiner
06-04-2013, 12:40
Eros, you're a genius! It works as fast as lightning now, even if I have twohundred-thousand vertices it's a speed-up of more than an incredible amount of million percents compared to for-next-loop - finding a certain vertex doesnt need longer than a few milliseconds and makes no remarkable break in program-flue while the looping through the array needs boring long time :D - I change now all my indicating-routines to this new syntax.
Thanks a lot for this
btw. I have always the latest available version installed - in order to use all the great new features that were added since V 1.8.6...
- who else is doing the beta-testing here? Please reply
:)
ErosOlmi
06-04-2013, 13:01
I'm happy it works fine.
Anyway before changing all your code, make some more tests.
Actually only = comparison is working. Honestly I do not know if other comparison tests (>, >=, <, <=, <>) have sense here.
I will review today and tomorrow because I think I can improve speed by another 50% avoiding some internal memory handling (in place comparison instead of copying memory and than comparing)
Testing: you are one of the best, that's sure.
There are also some hidden testers too :) They prefer not to use forum as interactive place where to exchange ideas or reporting.
There are also some colleagues at work reporting about feature requests and bugs. We insensitively use thinBasic for some jobs: data exchange with carriers, database updates when source information are text file, fast prototyping before making real applications, ...
ReneMiner
06-04-2013, 13:22
I don't think it would make sense to scan for unequals - and it already returns 0 if no equal elements. so this is fine. I implemented to different routines with different types now. All work as supposed to, no complains here.
Just one small other - more related to UDT, if I check if two vectors (.X,.Y,.Z) so both the same UDT contain the same values,
i have always to COMPARE all single elements because I can not ask
if Vec1 = Vec2 then
but I have to ask
if Vec1.X = Vec2.X and Vec1.Y = Vec2.Y and Vec1.Z = Vec2.Z and True then...
Now I think of comparing (just fixed size numeral UDT of course) like this:
Local sV1 as String * SizeOf(t_Vec3d) at Varptr(Vec1)
Local sV2 as String * SizeOf(t_Vec3d) at Varptr(Vec2)
if sV1 = sV2 then '... :)
would be better to use Union here? And how would I set up this for t_myType as above?
ErosOlmi
06-04-2013, 13:49
René
I've updated thinCore.DLL attached few posts before this.
New ARRAY SCAN ... for fixed strings arrays improved by 400% compare to the first version developed this morning.
On my PC from 0.013 to 0.003 ms to perform previous example
PLUS ...
Added a new function called MEMORY_COMPARE(pMem1, pMem2, Length) that can be used to compare two memory areas pointed by two memory pointers plus the len of the memory to test for equality.
You can, for example, use in this way:
MsgBox 0, memory_compare(VarPtr(foo(50000).x), VarPtr(foo(50001).x), SizeOf(Double) * 3)
It will return 0 if the two memory areas are equal, a number if the the two memory areas are different.
The number is the memory position where the two memory areas start to differ.
Hope this can be of help :)
Ciao
Eros
PS I'm going out for some time now. Will return later
ReneMiner
06-04-2013, 14:06
Thanks once more, of course I ran c:\thinBasic\thinAir\Syntax\thinBasic.CreateSyntaxIni.tBasic to get the memory_compare in blue+bold
but the result of 0 if match does not really fit, because if I write
if Memory_Compare (...) = 0 - very confusing- and the result says where it differs. so maybe change name from
Memory_Compare(...) to Memory_Differs(...) then my scripts reads like this
If Memory_Differs(VarPtr(foo(1)), VarPtr(foo(2)), SizeOf(my_type)) then ...
and also
If not Memory_Differs(A,B,SizeOf(abc)) then
ErosOlmi
06-04-2013, 15:35
Updated again thinCore.dll attached few posts before.
Now the arsenal is the following :)
Memory_Compare(pMem1, pMem2, Length)
Compares two areas of memory.
Returns 0 if they are equal
Byte position (starting from 1) if
they differs giving the position
where difference starts
Memory_Differs(pMem1, pMem2, Length)
Compares two areas of memory.
Returns %TRUE if they differs.
%FALSE if they are equal
Memory_IsEqual(pMem1, pMem2, Length)
Compares two areas of memory.
Returns %TRUE if they are equal.
%FALSE if they differs
Memory_Swap(pMem1, pMem2, Length)
Swaps two areas of memory.
pMem1 and pMem2 are pointers to memory areas.
ReneMiner
06-04-2013, 16:30
great. One of them (differs/isEqual) would have been enough - but why not. Now we can almost write plain-human readable text-scripts that a baby understands. I think thinBasic made another step into the future today :)
ErosOlmi
06-04-2013, 17:29
I was asking myself: what's the difference between an array of UDT and an array of fixed strings?
NOTHING! Just the interpretation of the memory.
So, why not avoiding to have to map an array of UDT into a fixed string virtual array?
I've updated again thinCore.DLL attached few posts before ( http://www.thinbasic.com/community/showthread.php?12056-Array-Scans-UDTs&p=88315&viewfull=1#post88315 )
in order to be possible the scan directly arrays of UDT allowing the following simplification.
From this:
'-------------------------------------------------------------------------
Function Get_Foo_ID(ByVal X As Double, Y As Double, Z As Double) As Long
'-------------------------------------------------------------------------
Static ToFind As t_myType
'---We want to find element having the following data
ToFind.x = x
ToFind.y = y
ToFind.z = z
If nFoos > 0 Then
'---Because array scan now works with fixed strings ...
'---Create a virtual array of fixed strings over our array of UDTs
Local sFoos(nFoos) As String * SizeOf(t_myType) At VarPtr(foo(1))
'---Just search from byte 11 for SizeOf(Double) * 3 bytes the equivalent byts in ToFind UDT
Function = Array Scan sFoos, Byte(11, SizeOf(Double) * 3), = ToFind
EndIf
End Function
to this
'-------------------------------------------------------------------------
Function Get_Foo_ID(ByVal X As Double, Y As Double, Z As Double) As Long
'-------------------------------------------------------------------------
Static ToFind As t_myType
'---We want to find element having the following data
ToFind.x = x
ToFind.y = y
ToFind.z = z
If nFoos > 0 Then
'---Just search from byte 11 for SizeOf(Double) * 3 bytes the equivalent byts in ToFind UDT
Function = Array Scan Foo, Byte(11, SizeOf(Double) * 3), = ToFind
EndIf
End Function
Hope you like it :D
ReneMiner
06-04-2013, 17:41
I build in Memory_Differs and Memory_IsEqual to several functions now already - saves a lot of lines and parenthesis and unnecessary indexing the same udt-variable over and over again and all works fine here.
Just for the others: new memory-methods can compare all but dynamic strings in udts, even if both udt-substrings have the same text- they have a different pointer...
btw. I don't know if anyone has read the issue i created (http://www.thinbasic.com/community/project.php?issueid=392#note2384) about dynamic strings in udt, but I think that explains why memory_differs will always say "TRUE" if dynamic string involved.
Edit: oops- I was typing while you already posted again... now I'm gonna check the new ones out...
Instantly tried out- all work fine :)
Array Scan now really great function - just about to become the one I want to build in everywhere now.
ErosOlmi
06-04-2013, 17:55
Dynamic strings inside UDT are not really "inside" the UDT :D
A dynamic string (BSTR) is a 4 bytes DWORD pointer to a dynamic memory area allocated/de-allocated as needed by the memory manager.
So inside the UDT you will always have 4 bytes pointer whose stored value will change every time the dynamic string is changed.
8170
This page gives some interesting info: http://oreilly.com/catalog/win32api/chapter/ch06.html
ReneMiner
06-04-2013, 18:30
I found some... mmmm- what about FOR?
Function = Array Scan Foo For nFoos, Byte(1, sizeOf(t_myType)), = toFind
Is For still valid - i just discovered if i set a point at 0,0,0 not as the first vertex, but my array is larger dimensioned, I get some invalid vertex because it returns the first empty -predimensioned- which gets overwritten later.
Now FOR still valid?
Edit: obviously...now figuring the continious way to sort out items in other udt-arrays already...
local lPos as long = 0
Do
lPos = Array Scan foo(1 + lPos) For nFoos - lPos, Byte(1, SizeOf(t_myType)), = toFind
if lPos Then
' do something with foo(lPos)...
Endif
Loop Until lPos = 0
ErosOlmi
06-04-2013, 19:13
Sorry René but I do not understand if there is a problem or not with For.
Checking thinBasic source code, For and starting index should work as expected.
ReneMiner
06-04-2013, 19:55
no problem at all. All works fine. I was just a little confused as your syntax-description for alternate syntax did not mention the FOR and I feared you forgot about it. But no reason - even the continious scan runs perfect and pretty fast - as long as I search for just all the same parameters...
The limit of use I reach in this type,
Type t_triangle
V(3) as Long
N(3) as Long
T(3) as Long
C(3) as Long
End Type
Dim Tri(123) as t_Triangle
because I have to scan 3 times here to find out if a certain vertex is used in some triangle. The vertex-ID can be found in either V(1), V(2) or V(3) and there's no possibility to OR here - but there I'll find another solution soon ;)
ReneMiner
06-04-2013, 20:34
Another "inflexibility" I now discovered: if I change the order in t_myType the Byte(START,....) becomes invalid and I have to go through all my script to fix that. I know from Purebasic the method
OffsetOf() - works the following:
Type t_myType
A as Long
B as Double
C as Single
'...
End Type
'now OffsetOf works like this:
Long StartOfA = OffsetOf(t_myType.A) ' wich will assign 0 to StartOfA - so this is the offset .A has from the actual varptr
Long StartOfB = OffsetOf(t_myType.B) ' wich will be equal to SizeOf(Long)
Long StartOfC = OffsetOf(t_myType.C) ' wich will be SizeOf(Long) + SizeOf(Double) then...
this could be a help to set the Starting-Byte correectly in any case
Petr Schreiber
06-04-2013, 20:40
Very interesting thread to go through, thanks Eros and Rene!
I like the systematic approach of OffsetOf, something like that would be useful addition to ThinBASIC in my opinion as well!
Petr
ReneMiner
07-04-2013, 11:27
I got a bug related to the FOR in Array Scan. I isolated it to a small console script. The calculations in this script are senseless but i have similar in my script to calculate vertex-normals and I wanted to keep the structure of the sub as it was in my app.
Says "Next without For For without Next" -
Uses "Console"
Dim testarray() As Long : ReDim testarray(123)
Dim i As Long
' just fill random array here
Randomize
For i = 1 To 123
testarray(i) = Rnd(10000)
Next i
PrintL "press key to run"
WaitKey
CheckNow()
PrintL "all done."
WaitKey
End
' ---------------------------------------------------------------------
Sub CheckNow()
Local li, lj, lk, lHave As Long
Local lSum As Long
Local lCounts As Long
Local lCollect(100) As Long
Local lTest As Long
If lHave < 1 Then Exit Sub
lTest = Rnd(777)
If lSum < 1 Then Exit Sub
For li = 1 To 123
lCounts = 0 : lSum = 0
For lj = 1 To 12
If TRUE Then
For lk = 1 To 3
If testarray(li) Then
lHave = 0
If lCounts Then lHave = Array Scan lCollect For lCounts, , = lTest
'also tried ... For lCounts, = lTest ... For lCounts, Byte(1, SizeOf(Long)), = lTest etc...
' seperated the if/result on seperate lines also. no change...
If Not lHave Then
lCounts += 1
lCollect(lCounts) = li
lSum += lTest
EndIf
EndIf
Next
EndIf
Next
If lCounts > 0 Then
If TRUE Then
For lj = 1 To 123
If TRUE Then
For lk = 1 To 3
If testarray(lj) = li Then
lSum -= 123
EndIf
Next 'lk
EndIf
Next ' lj
EndIf
EndIf
Next ' li
PrintL "...before exit..."
End Sub
So Array Scan invalid when using For inside For-Next-Loop?
ErosOlmi
07-04-2013, 12:59
I've updated again thinCore.DLL attached few posts before ( http://www.thinbasic.com/community/showthread.php?12056-Array-Scans-UDTs&p=88315&viewfull=1#post88315 )
FOR clause inside ARRAY SCAN ... problem mentioned in previous post: SOLVED
I've also added UDT_ElementOffset function that returns the offset of an element inside an UDT.
UDT_ElementOffset is able to scan the UDT and return offset of an element also for arrays of UDT
Important: UDT_ElementOffset accept an instantiated UDT variable and not just a defined TYPE
UDT_ElementOffset usage example:
Type t_myType
'---Just added some dummy bytes in order to demonstrate below array scan new BYTE option
aa As String * 10
'---This part will be the one we will search for.
X As Double
Y As Double
Z As Double
'---Here some other bytes
a As Long
b As Long
s As String * 100
'....
End Type
Dim nFoos As Long = 100000
Dim foo() As t_myType
ReDim foo(nFoos)
foo(100000).x = 1
foo(100000).y = 1
foo(100000).z = 1
foo(100000).s = "abc"
MsgBox 0, Udt_ElementOffset(foo(1).s)
MsgBox 0, Udt_ElementOffset(foo(100000).s)
ReneMiner
07-04-2013, 13:10
thats great. So it's even possible to retrieve position of a nested UDT as this?
Type t_inType
ABC as Long
end type
Type t_outType
boofoo(5) as t_inType
End Type
dim foo(123) as t_OutType
Long lPos = UDT_ElementOffset(foo(x).boofoo(2).ABC)
?
EDIT: Vertex-Normal calculation (as script-example above) works fine and fast :)
ErosOlmi
07-04-2013, 13:16
Well, the idea is that: function should be able to scan any UDT structure working into a thinBasic script and return element position depending on structure passed.
But ... :mad:
checking your example it GPF so I'm checking why.
:oops:
ErosOlmi
07-04-2013, 14:30
I've updated again thinCore.DLL attached few posts before ( http://www.thinbasic.com/community/showthread.php?12056-Array-Scans-UDTs&p=88315&viewfull=1#post88315 )
possibly fixed UDT_ElementOffset to work with more complex UDT structures
UDT_ElementOffset usage examples:
Type t_inType
ABC As Long
End Type
Type t_outType
boofoo(5) As t_inType
End Type
Dim foo(123) As t_OutType
Long boofooToChange = 3
Long fooToChange = 3
foo(fooToChange).boofoo(boofooToChange).ABC = 12
Long lPos
lPos = UDT_ElementOffset(foo(fooToChange).boofoo(boofooToChange).ABC)
Type t
a As Long
b As Double
c As String
d As String * 10
e As Double
f(10) As Byte
End Type
Dim x As t
lPos = UDT_ElementOffset(x.e)
lPos = UDT_ElementOffset(x.f(5))
Stop
ReneMiner
07-04-2013, 14:40
I don't want to become annoying nor a plague... but still something growing in my mind about array scan...
I have a lot of routines where I have to search if a vertex is member of any triangle or a triangle is member of a group etc.
So i have for example to check all 3 Tri(Index).V(Corner)'s if some desired Vertex-Index (XXX) is in any corner of the triangle in this case.
Local lPos as Long
'...
lPos = Array Scan Tri(1 + lPos) For nTriangles - lPos, Byte(UDT_ElementOffset(Tri(1).V(1)), SizeOf(Long), = XXX
'...
would give me just results from .V(1) that match XXX so have to run through 3 times at least to find all possible.
And if I have to know if two vertices, XXX and YYY are together in some triangle - gets already confusing - better use for-next -loop then?
would/could OR work here as ecpected by this:
lPos = Array Scan Tri(lPos + 1) For nTriangles - lPos, Byte(UDT_ElementOffset(Tri(1).V(1)), SizeOf(Long), = (XXX OR YYY)
???
Edit: you already posted whle I was typing, now I'll have a look at
ErosOlmi
07-04-2013, 15:30
You are not annoying nor a plague
Something like that can be acceptable:
Array Scan Foo, Byte(udt_elementoffset(foo(1).x), SizeOf(Double) * 3), = ToFind1, ToFInd2, ToFind3
Mainly the syntax would be the same as before but the number of buffers (UDT or strings) to check would be any:
Array Scan Foo, Byte(udt_elementoffset(foo(1).x), SizeOf(Double) * 3), = ToFind1 [, ToFind2 [, ToFindX ... ]]
The first found would return the position
Would be OK for you?
ReneMiner
07-04-2013, 15:34
would be very ok because if the position is known it's not a big deal to find out which one is in there - we have the adress of the parent, the child cannot hide from us :D
ErosOlmi
07-04-2013, 16:00
I've updated again thinCore.DLL attached few posts before ( http://www.thinbasic.com/community/showthread.php?12056-Array-Scans-UDTs&p=88315&viewfull=1#post88315 )
possibility to add multiple comparisons strings/UDT in Array Scan ... when used with UDT of fixed string arrays
Syntax is:
Array Scan Foo, Byte(udt_elementoffset(foo(1).x), SizeOf(Double) * 3), = ToFind1 [, ToFind2 [, ToFindX ... ]]
Dummy example:
'-------------------------------------------------------------------------
Function Get_Foo_ID(ByVal X As Double, Y As Double, Z As Double) As Long
'-------------------------------------------------------------------------
Static ToFind1 As t_myType
Static ToFind2 As t_myType
Static ToFind3 As t_myType
'---We want to find element having the following data
ToFind1.x = x
ToFind1.y = y
ToFind1.z = z
'---We want to find element having the following data
ToFind2.x = 2
ToFind2.y = 2
ToFind2.z = 2
'---We want to find element having the following data
ToFind3.x = 3
ToFind3.y = 3
ToFind3.z = 3
If nFoos > 0 Then
'---Just search from byte 11 for SizeOf(Double) * 3 bytes the equivalent byts in ToFind... UDT
Function = Array Scan Foo, Byte(udt_elementoffset(foo(1).x), SizeOf(Double) * 3), = ToFind1, ToFInd2, ToFind3
EndIf
End Function
ReneMiner
07-04-2013, 16:09
Now have thinCore(7).zip on my drive :D - how many parameters are valid for "toFind" now? guess there's a limit somewhere?
ErosOlmi
07-04-2013, 16:13
It loads a dynamic array as bigger as you will add a comma in the expression
I've tested up to 3 but should work with 30 (or 300) but at that point a different approach should be considered.
Petr Schreiber
07-04-2013, 18:46
Hi, I finally had a bit of time to try what are you cooking here.
Maybe it would be good to keep the UDT_ElementOffset consistent with what it does in other languages, please see this example:
Uses "Console"
Type tUDT
a As Byte
b As Integer
c As Long
End Type
Dim v As tUDT
v.a = 1
v.b = 2
v.c = 3
PrintL UDT_ElementOffset(v.a), "(expected 0, as it is in the root)"
PrintL UDT_ElementOffset(v.b), "(expected 1, because 0 + SizeOf(Byte) = 1)"
PrintL UDT_ElementOffset(v.c), "(expected 3, because 0 + SizeOf(Byte) + SizeOf(Integer) = 3)"
PrintL
PrintL "The following should print 1, 2, 3 I think,"
printl "but it is one Byte shifted, so it returns scary things now"
PrintL Peek(Byte , VarPtr(v) + UDT_ElementOffset(v.a))
PrintL Peek(Integer, VarPtr(v) + UDT_ElementOffset(v.b))
PrintL Peek(Long , VarPtr(v) + UDT_ElementOffset(v.c))
PrintL
WaitKey
My proposition:
UDT_ElementOffset to return 0 for first element - this is how it works in C, PureBasic, C# marshalling
UDT_ElementByte to return UDT_ElementOffset + 1 (this one would be usable with ARRAY SCAN as it is now).
Petr
ReneMiner
07-04-2013, 19:30
That explains a lot...
I'm fighting here since a few hours with some "Index out of bounds" and I didn't know where those invalid triangles came from and was just about to kill my cat for this... Always good to check the forum :)
Lucky cat
ErosOlmi
07-04-2013, 20:16
I've updated again thinCore.DLL attached few posts before ( http://www.thinbasic.com/community/showthread.php?12056-Array-Scans-UDTs&p=88315&viewfull=1#post88315 )
added UDT_ElementByte: returns the byte where the element starts
amended UDT_ElementOffset: returns the distance between the first byte of full structure to the element. So first element is at offset 0 (zero)
(I hate everything 0 base :onthequiet: )
Petr Schreiber
07-04-2013, 21:32
Thanks a lot :drink:
Petr
ReneMiner
07-04-2013, 22:24
Was a great weekend though, just checked out 1.9.5. and what's new. What would be interesting to know is how UDT_ElementsData_Join exactly works. I ask here because we already have all the others in this thread.
Could you give an example so I can imagine how it works?
ErosOlmi
07-04-2013, 22:57
It is more or less like JOIN$ but works on UDT elements.
It creates a string buffer with UDT elements or in case of UDt array a "quadratic" buffer: columns and rows.
In a script I developed at work I needed to export a big UDT array structure into a text file so I developed UDT_ElementsData_Join
In any case it has some limitations becasue at the moment is not able to scan UDT inside UDT
Type MyType
x As Long
y As Long
z As Long
End Type
Dim Mydata(2) As MyType
MyData(1).x = 1
MyData(1).y = 1
MyData(1).z = 1
MyData(2).x = 2
MyData(2).y = 2
MyData(2).z = 2
MsgBox 0, UDT_ElementsData_Join(MyData, $TAB, $CRLF)
zlatkoAB
08-04-2013, 17:36
I hate everything 0 base
So you indirectly hate any windows controls to ,because all of them are zero based
weird...:D
ErosOlmi
08-04-2013, 21:32
Good morning, I would like zero coffee.
Zero coffee?
Yes, zero coffee means 1 coffee.
???
Ok then, 1 dollar.
Here it is.
Hey: I said 1 dollar.
It is one dollar!
Yes 1 dollar means two dollars because it is the offset from you zero wallet.
:onthequiet:
ReneMiner
14-04-2013, 18:50
Somehow does not work with indexed UDT-subsets - or am I doing something wrong here?
Uses "Console"
Const %numTests = 123
Type t_Test
A As Long
B(3) As Long
End Type
Dim test(%numTests) As t_test
Dim lTest as Long = 777
test(17).B(1) = lTest
test(19).B(1) = lTest
test(25).B(2) = lTest
test(38).B(3) = lTest
test(74).B(3) = lTest
test(99).B(2) = lTest
Dim numMatches, lPos As Long
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(1)), SizeOf(Long)), = lTest
If lPos Then numMatches += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(2)), SizeOf(Long)), = lTest
If lPos Then numMatches += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(3)), SizeOf(Long)), = lTest
If lPos Then numMatches += 1
Loop Until lPos = 0 Or lPos >= %numTests
PrintL "await 6 matches"
PrintL "found" + Str$(numMatches) + " matches"
WaitKey
more crazy gets if i use this :
'...
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(1 2 3)), SizeOf(Long)), = 777
or this:
' ...
Dim lTest as Long = 1
' ...
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(1 2 3)), SizeOf(Long)), = lTest
of course I didn't use " .B(1 2 3) " but I won't write it three times here
also different result:
Dim lTest as Long = &H7FFFFFFF
Edit: total Mess-up if use this:
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(%numTests).B(1 2 3)), SizeOf(Long)), = lTest
so might have something to do with the Index of UDT_ElementByte(Test( XXX )...
ErosOlmi
14-04-2013, 20:14
I'm checking ...
For sure it will not find any 777 because ARRAY SCAN ... BYTE ... compares pieces of memory and not numbers
so it would be necessary to scan for MKD$(777) or MKD$(lTest)
Anyway I confirm there must be something else.
ReneMiner
14-04-2013, 20:49
I think MKD$ is not the right choice for a long since it has double the size...
But if I use MKL$ results still wrong... I found that out during the round-vertices function. First I thought "Round"-method would not work but it did'nt change any of my triangles - so I went back to a for-next loop in the meantime.
Anyway: If I go 3 times by array scan seems still much faster then one time for-next through the same count of elements.
And I doubled the amount of scans and values... be surprised - the result doubles too:
Uses "Console"
Const %numTests = 100
Type t_Test
A As Long
B(3) As Long
End Type
Dim test(%numTests) As t_test
Dim lTest As Long = &H7FFFFFFF
Dim lTes2 As Long = 100000
test(17).B(1) = lTest
test(19).B(1) = lTest
test(25).B(2) = lTest
test(38).B(3) = lTest
test(74).B(3) = lTest
test(99).B(2) = lTest
test(15).B(1) = lTes2
test(29).B(1) = lTes2
test(33).B(2) = lTes2
test(47).B(3) = lTes2
test(54).B(3) = lTes2
test(89).B(1) = lTes2
Dim numMatches1, numMatches2, lPos As Long
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(1)), SizeOf(Long)), = MKL$(lTest)
If lPos Then numMatches1 += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(2)), SizeOf(Long)), = MKL$(lTest)
If lPos Then numMatches1 += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(3)), SizeOf(Long)), = MKL$(lTest)
If lPos Then numMatches1 += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0 '<< i forgot this here, now I set it in, still same result
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(1)), SizeOf(Long)), = MKL$(lTes2)
If lPos Then numMatches2 += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(2)), SizeOf(Long)), = MKL$(lTes2)
If lPos Then numMatches2 += 1
Loop Until lPos = 0 Or lPos >= %numTests
lPos = 0
Do
lPos = Array Scan test(lPos + 1) For %numTests - lPos, _
Byte(UDT_ElementByte(test(1).B(3)), SizeOf(Long)), = MKL$(lTes2)
If lPos Then numMatches2 += 1
Loop Until lPos = 0 Or lPos >= %numTests
PrintL "ltest await 6 matches"
PrintL "found" + Str$(numMatches1) + " matches"
PrintL "lTes2 await 6 matches"
PrintL "found" + Str$(numMatches2) + " matches"
WaitKey
ErosOlmi
14-04-2013, 21:11
Sorry, yes is MKL$
Anyway the problem is that ARRAY SCAN compares the same byte/len position both in the array and in the compared buffer.
If you start at byte position 5 len 4 in the array, the same memory area will be taken from the comparison buffer
So you have to write something like:
...
'---Define a comparison buffer and set the requested data you want to check
Dim comp As t_test
comp.b(1) = 777
...
'---Than compare
...
lPos = Array Scan test(lPos + 1), Byte(UDT_ElementByte(test(1).B(1)), SizeOf(Long)), = comp
...
ReneMiner
14-04-2013, 21:13
ok, sure. If that's the rule then I did it wrong. I just assumed it would be possible to compare the plain value.
Edit: I tried, seems still not to find all.
ErosOlmi
14-04-2013, 21:19
Maybe it is the case I will add an option NOT to compare symmetric position/size in both array and buffer.
Maybe using ASYM keyword
Something like:
lPos = Array Scan test(lPos + 1), Byte(UDT_ElementByte(test(1).B(1)), SizeOf(Long)), = AnyBuffer ASYM
ReneMiner
14-04-2013, 21:38
What I wonder about:
I give the "UDT_ElementByte(ABC.X)" which tells it's a variable of whatever .X is.
Now why do I have to give a length (as SizeOf) when I just scan this ".X"-element which size is known to thinBasic and also the size of the thing to compare with - is known - and they both should match... Might be different when scanning Strings of any kind, but for fixed-size numerals? Is there reallly a length needed when comparing just one?
ErosOlmi
14-04-2013, 21:43
Well, the ARRAY SCAN ... BYTE(start, len) is just a "generic" scan option able to work in many different situations.
I will think about some more specific scan options for UDT structures.
ReneMiner
14-04-2013, 21:45
I know, that needs time to be done correctly :)
Always here for testing.
ReneMiner
15-04-2013, 10:12
Eros, I think I got a suggest for this (or a direction to think into)
now we have
Array Scan sTest, Collate Ucase, = matchString ' scan for a alphanumerical String
Array Scan foo, Byte(start, length) '... scans perfectly a whole type and sequences with more than one byte
' how about
array scan fooL, Long(UDT_ElemtentByte(fooL.X)), = MKL$(LongtoCompare)
array scan fooD, Double(start), = MKD$(DoubletoCompare)
'?
Edit: Just one small other thing - I was reminded here because the "Ucase"...
If I write in thinAir
LCASE$ looks like LCase$
UCASE$ looks like Ucase$
I think that comes from the "Ucase" used in Collate-switch - always makes me look twice because the $ does not bold.
And it won't help to create the thinAir-syntax.ini in a different order i guess, because both are from thinCore... now I think about some small utility that re-orders syntax.ini so longest keywords come first. I'll report if helps...
(Answer: No - does not change if UCase$ is before Ucase in thinBasic_Keywords.ini )
EDIT 2:
(I leave old suggest as it was ,so maybe easier to follow my thoughts.)
omit MKx$ completely and compare real value, so it will be possible as this
array scan fooD, Double(start), = DoubletoCompare
array scan fooD, Double(start), >= DoubletoCompare
array scan fooD, Double(start), <> DoubletoCompare 'etc...
ReneMiner
16-04-2013, 12:42
The function for my testing here was in
TBGL3dEd/Specific3d.tBasicU\Sub Mesh_RoundVertexCoords
the code is still in there commented and replaced through the for-next-loop in the lower area of the sub.
(attention when uncommenting: line 1663 'lNewVec = VecID.... should not be uncommented then because this is alread done another way above)
Search for Triangles - Tri() - which have a number li in either Tri(1 to nTriangles).V(1), .V(2) or .V(3)
For test, draw some circle-shape or load them,
then hold CTRL+Leftclick onto any corner of any circle-shape to jump there, read position from the coordinates in bottom-area. Mostly will be non-integers when it is circular shape - anyway, jump some corner that has non-integer-position in any coordinate,
then Type 'M' or click "Mesh" on menu,
set Decimals to round to low as 0 or 1
click Round-Vertices-Button
jump to same corner again by leftclicking while holding CTRL, read coordinates. Are they rounded as desired? then function works.