- Contents in this wiki are for entertainment purposes only
QAT Code: SLIP
Jump to navigation
Jump to search
About ∞ White paper ∞ QuasiMatrix Code ∞ QAT Code ∞ Data Examples ∞ Scatter-Gather Explained ∞ The Plow Share ∞
The QAT Code — VB6 SLIP class module
The Warehouse if Pairs Expansion then Visual Scatter else Visual Cluster redim Bracketing Clusters while Event Chemistry wend SLIP class redim
'--------------------------------------------------------------------------------
' Component : clsSLIP class module
' Project : QAT
' Copyright : DonEMitchell 2001-2011 Sequim, WA - Cottonwood, AZ
'
' Prueitt-Mitchell (2011): Completion of the QAT Ontology engine.
' Version 2.8.X edits opened October 27, 2011
' Modified : 10/27/2011-2:29:30 PM on Developer
'--------------------------------------------------------------------------------
Private mcolTopicTree As Collection ' Holds name-keyed references to clsTopic instances.
Private moTopicSelection As clsTopic ' Reference object of user-selection.
Private moAtoms As clsAtoms
' Change-events called by this class (invokes event code in the user form when 'raised' in this class)
Public Event TopicCreated(ByRef oTopic As clsTopic)
Public Event TopicSelected(ByRef oTopic As clsTopic)
Public Event PairsImported(ByRef oRoot As clsTopic)
Public Event ReportKeyChanged(ByVal KeyColumn As Long)
Public Event AtomRadialDistributionChanged(ByRef oTopic As clsTopic)
Public Event Atom3DDistributionChanged(ByRef oTopic As clsTopic)
Public Event RemarkAppended(ByVal NewLine As String)
Public Event CommandResponse(ByVal Message As String)
Public Event Progress(ByVal Message As String)
Private mstrPairsArray() As String
Private mstrAtomsStringBuffer As String
Private mstrPairsStringBuffer As String
Private mbooSourceLoaded As Boolean
Private mbooPairsImported As Boolean
Private mbooReportGenEnabled As Boolean
Private mintReportSortColumn As Integer
Private mlngReportKeyColumn As Long
Private mintLevelCount() As Integer ' Array stores topics-count per level for spacing the TG nodes.
Private mintMickey As Integer ' Mouse-button flag.
Private mstrDataPath As String
Private mstrEventBrowser As String
' State-flags
Private mbooIterating As Boolean
Private Sub Class_Initialize()
On Error Resume Next
' Data-structure ' Functionality:
Set mcolTopicTree = New Collection ' A collection of topic instances by Name, affording tree-hiearchy-navigation through Parent property of clsTopic.
Set moAtoms = New clsAtoms
Set moTopicSelection = New clsTopic
End Sub
Private Sub Class_Terminate()
On Error Resume Next
Set mcolTopicTree = Nothing
Set moAtoms = Nothing
Set moTopicSelection = Nothing
End Sub
Public Sub INILoadProperties()
On Error Resume Next
mstrDataPath = INIGetString("SLIP", "RootTopicName", cDefTopTopicName)
mbooSaveProperties = INIGetBool("SLIP", "SaveProperties", "1")
End Sub
' Saves the default variables to a private profile (QAT.INI file)
Public Sub INISaveProperties()
Dim lngResult As Long
Dim lngCount As Long
' Save Properties, optionally.
INIWrite "SLIP", "SaveProperties", CStr(mbooSaveProperties)
If mbooSaveProperties Then
INIWrite "SLIP", "RootTopicName", mstrDataPath
End If
End Sub
Public Function IsTopic(ByVal Name As String) As Boolean
' Return True if Name matches at least one name in the Topic collection.
Dim topicX As clsTopic
' Loop through the topic-object collection, comparing names until the first match.
For Each topicX In mcolTopicTree
If StrComp(topicX.Name, Name, vbTextCompare) = 0 Then 'a case-insensitive match (first)
IsTopic = True
Exit For
End If
Next
End Function
Public Function topicFindTopic(ByVal Name As String) As clsTopic
' This assumes all topic names are unique
Dim topicX As clsTopic
On Error Resume Next
For Each topicX In momcolTopicTree
If StrComp(Name, topicX.Name, vbTextCompare) = 0 Then
Set topicFindTopic = topicX
Exit For
End If
Next
End Function
Public Function topicMembersFile(ByVal oTopic As clsTopic) As String
End Function
Public Function topicSortedReport(ByRef oTopic As clsTopic) As String
' Load the Report.txt contents.
' Split into an array.
' Sort by ReportSortColumn.
' Re-concatenate a CR delimited report string and return it.
Dim ndx As Long
Dim ndxDeepest As Long
Dim strFilespec As String
Dim strReport As String
Dim strReportHeader As String
Dim strReportArray() As String 'array
Dim strFirst As String
Dim strSecond As String
Dim strTemp As String
On Error GoTo Trap
' Load the Report.txt file
strFilespec = Root.WhsePath & oTopic.Path & "\Report.txt"
With New clsFile
If .FileExists(strFilespec) Then
' The report now exists by the same filespec, read it into variables
strReportHeader = .ReadLine
strReport = Mid$(.ReadAll, Len(strReportHeader) + 3) ' read everything past the header line
' Remove trailing outer delimiters
TrimTrailingCharacters strReport, vbCrLf
' Split into an array at the outter delimiting CRs
strReportArray = Split(strReport, vbCrLf)
' Sort the Array on ReportSortColumn
DataSortStringArray strReportArray
' Now concatenate an output string from the sorted array
' Start with a header
topicSortedReport = strReportHeader & vbCrLf
' concatenate each array element with CR delimitation
For ndx = 0 To UBound(strReportArray)
topicSortedReport = topicSortedReport & strReportArray(ndx) & vbCrLf
Next ndx
' Remove trailing outer delimiter from concatenation
topicSortedReport = modMain.TrimTrailingCharacters(topicSortedReport, vbCrLf)
.CloseFileRead
' Save to file
If Not .Save(topicSortedReport, strFilespec) Then
RaiseEvent CommandResponse("Unable to save the sorted report to file " & strFilespec)
MsgBox "Unable to save the sorted report to file " & strFilespec, vbExclamation
End If
End If
End With
ExitPoint:
Exit Function
Trap:
topicSortedReport = "Error encountered: Unable to sort " & oTopic.Name & " on column " & DataReportSortColumn & " of columns 0-7"
RaiseEvent CommandResponse("Unable to sort " & oTopic.Name & " on column " & DataReportSortColumn & " of columns 0-7")
Resume ExitPoint
Resume
End Function
Public Function topicTopic(ByVal Path As String) As clsTopic
On Error Resume Next
Set topicTopic = momcolTopicTree.Item(Path)
End Function
Public Function topicTopicRemark(ByVal oTopic As clsTopic) As String
End Function
Public Function ReportWhseRecords(ByRef Atoms As clsAtoms) As String
' Extract all warehouse records that match to any
' atom name in the MatchColumn specified by DataReportKeyColumn
' Return the report-string.
Dim strWhse As String
Dim strRecords() As String 'array
Dim strFields() As String 'array
Dim atomX As clsAtom
Dim ndx, ndxProgress As Long
Dim strReportFilespec As String
Dim strReport As String
On Error Resume Next
RaiseEvent CommandResponse("Generating a warehouse data report...")
' Enable the report loop
mbooReportGenEnabled = True
strReportFilespec = mstrDataPath & "\" & "WhseReport.txt"
With New clsFile
If .FileExists(Root.WhsePath & "\Datawh.txt") Then 'redundant existence test, but validates folder edits
' Read the warehouse data file contents to a working string
strWhse = .ReadAll
' Remove all trailing outer-delimiters from the whse
modMain.TrimTrailingCharacters strWhse, vbCrLf
' Split the warehouse data string into an array at the CRs
strRecords = Split(strWhse, vbCrLf)
' Start the report with the header line from the whse
strReport = strRecords(0) & vbCrLf
' Convention requires the warehouse data file
' contains column names as line one, therefore
' array element zero contains column names.
' Loop through all atoms, using the atom
' name as the look-up key for matches
' of the atom name with the specified key column
For Each atomX In Atoms
' Loop through all records, and
' examine the MatchColumn specified for a match with the Atom name
' Concatenate the matching records as a
' sub-set of the Warehouse, and return the sub-set
If mbooReportGenEnabled Then
ndxProgress = ndxProgress + 1
RaiseEvent Progress(CStr(ndxProgress) & ":" & CStr(ndx))
' Yield to system needs (systemic affordance)
' allowing report cancellation with Stop command
DoEvents
For ndx = 1 To UBound(strRecords) 'skip element zero, the whse column header
If ndx Mod 5000 = 0 Then RaiseEvent Progress(CStr(ndxProgress) & ":" & CStr(ndx))
DoEvents
If Not mbooReportGenEnabled Then Exit Function
' Split the record into an array at the Tabs
strFields = Split(strRecords(ndx), vbTab)
If Len(strRecords(ndx)) Then
' Check for a match with key on the specified MatchColumn
' and concatenate the return as a CR delimited string
If StrComp(strFields(DataReportKeyColumn), atomX.Name) = 0 Then
strReport = strReport & strRecords(ndx) & vbCrLf
End If
End If
Next ndx
' Save the report while in progress of looping through atoms
With New clsFile
If .Save(strReport, strReportFilespec) Then
' Reselect
Set Context.TopicSelection = Context.TopicSelection
RaiseEvent CommandResponse("Unable to save the report on member " & atomX.Name & " to file " & strReport)
End If
End With
Else
MsgBox "Report aborted", vbInformation
RaiseEvent CommandResponse("Report generation aborted")
Exit For
End If ' mbooReportGenEnabled
Next atomX
' Notify interested agencies
RaiseEvent CommandResponse("Report generated from warehouse records for matches in column " & CStr(DataReportKeyColumn) & " (zero-base)")
' Remove the trailing CR
strReport = modMain.TrimTrailingCharacters(strReport, vbCrLf)
Else
RaiseEvent CommandResponse("Data warehouse not available: " & Root.WhsePath & "\Datawh.txt")
MsgBox "Data warehouse not available: " & Root.WhsePath & "\Datawh.txt"
End If
' Return the report
ReportWhseRecords = strReport
End With
End Function
'
'Public Function CreateAttribute(ByVal Name As String, ByVal Value As String) As clsAttribute
' On Error Resume Next
'
' Set CreateAttribute = New clsAttribute
' With CreateAttribute
' .Name = Name
' .Value = Value
' End With
'End Function
' Instance constructors
Public Function CreateTopTopic( _
ByVal Name As String, _
ByVal x As Long, _
ByVal Y As Long) As clsTopic
Dim oNew As clsTopic
On Error Resume Next
Set oNew = New clsTopic 'create a concrete type for return
With oNew
.Name = Name
.MetricsX = x
.MetricsY = Y
.RadialMagnification = 30 'Default
Set .Atoms = DataLoadMembers(Root.WhsePath & "\" & Name & "\Members.txt")
End With
' Add a reference in the TopicSpace
mcolTopicTree.Add oNew, oNew.Path 'keyed by path
' Notify the world
RaiseEvent TopicCreated(oNew)
' Return the new member
Set CreateTopTopic = oNew
Set oNew = Nothing
End Function
Public Function CreateTopic( _
ByVal Name As String, _
ByVal x As Long, _
ByVal Y As Long, _
oParent As clsTopic) As clsTopic
' Construct a concrete clsTopic instance.
Dim oNewTopic As clsTopic
On Error Resume Next
Set oNewTopic = New clsTopic
With oNewTopic
.Name = Name
Set .TopicParent = Me
Set .Atoms = DataLoadMembers( _
MembersFileSpec:=Root.WhsePath & oParent.Path & "\" & Name & "\Members.txt")
.MetricsX = x
.MetricsY = Y
.RadialMagnification = 30 'larger default for non-root levels
End With
' Add a reference to the new topic in the Children
' collection of the Parent Topic, keyed by Name
' This creates parent-child linking
oParent.TopicChildren.Add oNewTopic, oNewTopic.Name
' Add a reference in the TopicSpace keyed by path
mcolTopicTree.Add oNewTopic, oNewTopic.Path 'keyed by path
' Notify the world
RaiseEvent TopicCreated(oNewTopic)
Set CreateTopic = oNewTopic
Set oNewTopic = Nothing
End Function
Public Function DataAppendRemarkFile( _
ByVal NewLine As String, _
ByVal Filespec As String, _
Optional SendEventMsg As Boolean = True) As Boolean
' Save the file and send a RemarkAppended message, maybe
On Error GoTo Trap
With New clsFile
.OpenForAppend Filespec
.WriteLine NewLine
If SendEventMsg Then RaiseEvent RemarkAppended(NewLine)
RaiseEvent CommandResponse("Remark line appended")
End With
DataAppendRemarkFile = True
ExitPoint:
Exit Function
Trap:
'Append contextual information into existing err object Description
Err.Description = "SLIP.clsSLIP.DataAppendRemarkFile: " & Err.Description & " - File: " & Filespec
Err.Raise Err.Number
Resume ExitPoint
End Function
Public Property Get DataAtoms() As clsAtoms
Set DataAtoms = moAtoms
End Property
Public Function DataReadReportKey() As Boolean
Dim strConjecture As String
On Error GoTo Trap
With New clsFile
' Read the conjecture file for the report key column (B)
If .FileExists(mstrDataPath & "\Conjecture.txt") Then
strConjecture = .ReadAll()
DataReportKeyColumn = Split(strConjecture, vbTab)(0)
RaiseEvent CommandResponse("Report key Column set to " & CStr(DataReportKeyColumn))
DataReadReportKey = True
Else
DataReportKeyColumn = 4
RaiseEvent CommandResponse("Conjecture data not found -defaulting report key column to 4")
End If
End With
RaiseEvent CommandResponse("Reading ReportKeyColumn from Conjecture data")
Exit Function
Trap:
RaiseEvent CommandResponse("Report Key Column not set. Error reading " & mstrDataPath & "\Conjecture.txt" & Err.Description)
End Function
Public Property Get DataPairsArray() As String()
DataPairsArray = mstrPairsArray
End Property
Public Property Let DataReportGenerationEnabled(ByVal RHS As Boolean)
mbooReportGenEnabled = RHS
End Property
Public Property Get DataReportGenerationEnabled() As Boolean
DataReportGenerationEnabled = mbooReportGenEnabled
End Property
Public Property Get DataReportKeyColumn() As Long
DataReportKeyColumn = mlngReportKeyColumn
End Property
Public Property Let DataReportKeyColumn(ByVal Value As Long)
mlngReportKeyColumn = Value
RaiseEvent ReportKeyChanged(Value)
End Property
Public Property Let DataReportSortColumn(ByVal RHS As Integer)
mintReportSortColumn = RHS
End Property
Public Property Get DataReportSortColumn() As Integer
DataReportSortColumn = mintReportSortColumn
End Property
Public Sub DataRotateAtoms3DUnused(oTopic As clsTopic, ByVal Rotation As Integer, ByVal Incline As Integer)
' Convert each (x,y,z) point to a vector (angleI, angleR, length)
' Increment the cyphered vector by the passed parameters
' Convert the incremented vector back to a Cartesian coordinates
' and update the atom with the new (x,y,z) coordinates
Dim atomX As clsAtom
Dim x, Y, Z As Single
For Each atomX In oTopic.Atoms
Next
End Sub
Public Sub DataSortStringArray(ByRef SourceArray() As String)
Dim ndx, ndxDeepest As Long
Dim strFirst As String
Dim strSecond As String
Dim strTemp As String
ndx = 0
On Error Resume Next
' Sort the Array on KeyColumn into ordinal rank predicated by string compare method
Do While ndx < UBound(SourceArray) - 1
' Retreive the keycolumn values of two successive array elements which are split by tabs
strFirst = Split(SourceArray(ndx), vbTab)(mintReportSortColumn) 'default to zero, temporal sort
strSecond = Split(SourceArray(ndx + 1), vbTab)(mintReportSortColumn)
Select Case StrComp(strFirst, strSecond)
Case -1, 0 ' ndx1 <= ndx2
ndx = ndxDeepest + 1
ndxDeepest = ndx
Case 1 'ndx1 > ndx2
' Swap the inordinate pair
strTemp = SourceArray(ndx + 1)
SourceArray(ndx + 1) = SourceArray(ndx)
SourceArray(ndx) = strTemp
' Store the deepest progress
If ndx > ndxDeepest Then ndxDeepest = ndx
' Backup the index by one
' to reinspect the prior pair,
' unless its the top element 1
If ndx > 0 Then ndx = ndx - 1
End Select
'Debug.Print CStr(ndx) & String(ndx + 1, "-") & "+"
Loop
End Sub
'Public Property Get DataWarehouseArray() As String()
' DataWarehouseArray = mstrPairsArray
'End Property
'
Public Property Get DataAtomsStringBuffer() As String
DataAtomsStringBuffer = mstrAtomsStringBuffer
End Property
Public Function DataClearTopicGraph() As Boolean
' Clear everything but
' the Functionality Singleton instance
' the GUID, as this instance still exists
' and the TopicMetrics fields, of the top Topic,
' being situated context information.
Set mcolTopicTree = New Collection
Set ContextTopicSelection = Nothing
mbooIterating = False
ReDim mintLevelCount(0)
mstrAtomsStringBuffer = ""
mstrPairsStringBuffer = ""
mbooSourceLoaded = False
mbooPairsImported = False
ReDim mstrPairsArray(0)
mbooIterating = False
mbooReportGenEnabled = True
mintReportSortColumn = cDefWhseRepSortCol
mlngReportKeyColumn = cDefWhseRepKeyCol
ReDim mintLevelCount(0)
mintMickey = 0
mstrDataPath = ""
mstrEventBrowser = ""
' Send a message.
RaiseEvent CommandResponse("Topic Graph Cleared")
End Function
Public Sub DataClusterAtomsRadial(ByRef oTopic As clsTopic, Optional ByVal MaxKiloIters As String = "100")
Dim AtomA As clsAtom
Dim AtomB As clsAtom
Dim intGap As Integer
Dim intDifference As Integer
Dim ndx As Long 'max value past four billion
Dim strPair As String 'holds "**" delimited pair-names of two randomly selected Atoms
Dim atomX As clsAtom
Dim strMember As String
Dim intAngle1 As Integer
Dim intAngle2 As Integer
Dim strTemp As String
Dim strTabulation As String
Dim lngMaxIters As Long
Dim lngStartingIters As Long
Dim col As Collection 'pair counter with hash
'Const cHeat = 0.05
'Const cHeat = 0.3
Const cHeat = 0.67
'Const cHeat = 1
'Const cHeat = 1.33
On Error Resume Next
Set col = New Collection
Screen.MousePointer = vbArrowHourglass ' because DoEvents is in a loop,
' allowing IterationEnabled to be toggled
' while iterating
' Cypher stop point
lngMaxIters = CSng(MaxKiloIters) * 1000
lngStartingIters = oTopic.RadialIters
RaiseEvent CommandResponse("Clustering in progress...")
' Loop until disabled (grammatical Stop)
Randomize ' Randomize the seed of the Rnd statement
DataIterationEnabled = True
While mbooIterating And (ndx < lngMaxIters)
DoEvents 'allowing IterationEnabled to be toggled
With oTopic.Atoms
' Randomly select two atoms
Set AtomA = .Item(CInt(Rnd * (.Count - 1)) + 1) 'retrieve Atom by random ordinal
Set AtomB = .Item(CInt(Rnd * (.Count - 1)) + 1)
End With
' Build a name-pair from randomly selected Atoms
strPair = AtomA.Name & "**" & AtomB.Name
'col.Add strPair, strPair
'If Err = 0 Then
' Debug.Print col.Count; strPair
'End If
Err.Clear
' Seek for the presence of the pair in the tabular pairs buffer
' If found the pair is moved closer together
If PairExists(strPair) Then
'Debug.Print "Pair count: " & CStr(col.Count)
' Move the two atoms closer,
' leaving the separation at
' cHeat portion of the original radial gap
' Store the smaller degree as Angle1 and larger degree as Angle2
If (AtomA.Degree < AtomB.Degree) Then
intAngle1 = AtomA.Degree
intAngle2 = AtomB.Degree
Else
intAngle1 = AtomB.Degree
intAngle2 = AtomA.Degree
End If
' Store the difference in angles
intGap = intAngle2 - intAngle1
Select Case (intGap)
Case Is <= 180
intAngle1 = (cHeat * intGap / 2) + intAngle1
intAngle2 = intAngle2 - (cHeat * intGap / 2)
Case Is > 180
intGap = 360 - intGap
intAngle1 = intAngle1 - (cHeat * intGap / 2)
intAngle2 = (cHeat * intGap / 2) + intAngle2
End Select
' Normalize the RadialAngle, should any have lapsed negative
If intAngle1 < 0 Then intAngle1 = intAngle1 + 360
If intAngle1 > 360 Then intAngle1 = intAngle1 - 360
If intAngle2 < 0 Then intAngle2 = intAngle2 + 360
If intAngle2 > 360 Then intAngle2 = intAngle2 - 360
AtomA.Degree = intAngle1
AtomB.Degree = intAngle2
End If 'PairExists(strPair)
' Increment the index
ndx = ndx + 1
' Every 10000 iterations,
' or every attainment of MaxIters,
' save and update the plot
If (ndx Mod 10000) = 0 Or ndx >= lngMaxIters Then
' Update the memory instance of oTopic
oTopic.RadialIters = lngStartingIters + ndx
'Debug.Print atomA.Name; atomB.Name
'Debug.Print oTopic.RadialIters; ndx
' Send notifacation message
RaiseEvent AtomRadialDistributionChanged(oTopic)
' Save the Members tabulation to file
With New clsFile
' Convert the Atoms object to a string
strTabulation = Tabular.SerializeMembers(oTopic.Atoms)
' Save the string as the current iterated state of members
.Save _
Buffer:=strTabulation, _
Filespec:=mstrDataPath & oTopic.Path & "\Members.txt"
.CloseFileWrite
' Update the user interface.
RaiseEvent AtomRadialDistributionChanged(oTopic)
Stop
End With
End If
Wend
oTopic.RadialIters = lngStartingIters + ndx
DataIterationEnabled = False
RaiseEvent CommandResponse("Clustering iterations completed: " & CStr(ndx))
Screen.MousePointer = vbDefault
End Sub
Public Sub DataClusterAtoms3D(ByRef oTopic As clsTopic, Optional ByVal MaxKiloIters As String = "100")
Dim AtomA As clsAtom
Dim AtomB As clsAtom
Dim intGap As Integer
Dim intDifference As Integer
Dim ndx As Long 'max value past four billion
Dim strPair As String 'holds "**" delimited pair-names of two randomly selected Atoms
Dim atomX As clsAtom
Dim strMember As String
Dim intAngle1 As Integer
Dim intAngle2 As Integer
Dim strTemp As String
Dim strTabulation As String
Dim lngMaxIters As Long
Dim lngStartingIters As Long
Dim intMoves As Integer
'Const cHeat = 0.05
'Const cHeat = 0.3
Const cHeat = 0.67
'Const cHeat = 1
'Const cHeat = 1.33
On Error GoTo Trap
' Cypher stop point
lngMaxIters = CLng(MaxKiloIters) * 1000
lngStartingIters = oTopic.RadialIters
RaiseEvent CommandResponse("Clustering in progress...")
Randomize ' Randomize the seed of the Rnd statement
DataIterationEnabled = True
' Loop until disabled (grammatical Stop)
While mbooIterating And (ndx < lngMaxIters)
DoEvents 'allowing IterationEnabled to be toggled
With oTopic.Atoms
' Randomly select two atoms
Set AtomA = .Item(CInt(Rnd * (.Count - 1)) + 1) 'retrieve Atom by random ordinal
Set AtomB = .Item(CInt(Rnd * (.Count - 1)) + 1)
End With
' Build a name-pair from randomly selected Atoms
strPair = AtomA.Name & "**" & AtomB.Name
' Seek for the presence of the pair in the tabular pairs buffer
' If found the pair is moved closer together
If PairExists(strPair) Then
' Reduce the 3D-space distance between the pair
'newDistance = Metrics.AtomicDistance(AtomA, AtomB) / cHeat
With AtomA
.MetricsX = .MetricsX + cHeat * (AtomB.MetricsX - .MetricsX) / 2
.MetricsY = .MetricsY + cHeat * (AtomB.MetricsY - .MetricsY) / 2
.MetricsZ = .MetricsZ + cHeat * (AtomB.MetricsZ - .MetricsZ) / 2
End With
With AtomB
.MetricsX = .MetricsX - cHeat * (.MetricsX - AtomA.MetricsX) / 2
.MetricsY = .MetricsY - cHeat * (.MetricsY - AtomA.MetricsY) / 2
.MetricsZ = .MetricsZ - cHeat * (.MetricsZ - AtomA.MetricsZ) / 2
End With
End If 'PairExists(strPair)
' Increment the index
ndx = ndx + 1
' Every 10000 iterations,
' or every attainment of MaxIters,
' save and update the plot
If (ndx Mod 10000) = 0 Or ndx >= lngMaxIters Then
' Update the memory instance of oTopic
oTopic.RadialIters = lngStartingIters + ndx
'Debug.Print atomA.Name; atomB.Name
'Debug.Print oTopic.RadialIters; ndx
' Send notifacation message
RaiseEvent Atom3DDistributionChanged(oTopic)
'GoTo fu
' Save the Members tabulation to file
With New clsFile
' Convert the Atoms object to a string tabulation
strTabulation = Tabular.SerializeMembers(oTopic.Atoms)
' Save the Members tabulation to file
.Save _
Buffer:=strTabulation, _
Filespec:=mstrDataPath & oTopic.Path & "\Members.txt"
.CloseFileWrite
' Send notifacation message
RaiseEvent Atom3DDistributionChanged(oTopic)
End With
fu:
End If
Wend
oTopic.RadialIters = lngStartingIters + ndx
RaiseEvent CommandResponse(CStr(ndx) & " 3D Clustering iterations completed")
ExitPoint:
Exit Sub
Trap:
Resume ExitPoint
Resume
End Sub
Function PairExists(ByVal Pair As String) As Boolean
' Pair is compared against the middle, then successive
' binary division of remaining middles on either
' left or right of the compare point,
' until the exact match is found, which returns true,
' or until there are no further records to divide, returning false
' The mstrPairsArray is assumed to be sorted in ascending rank
' The ordination of rank among the array names then become a
' self-index into the array by value of the array element, based on a successive binary division
' of left or right halves, pending a string comparison returning
' a three-state result: less, equal or greater.
Dim lngSmaller As Long
Dim lngMiddle As Long
Dim lngLarger As Long
Dim ndxAttempts As Integer
On Error Resume Next
lngSmaller = 0
lngLarger = UBound(mstrPairsArray)
'Debug.Print "Total Pairs: " & UBound(mstrPairsArray) + 1
Do While lngSmaller < lngLarger
' Place a middle ndx in the current bounded block
lngMiddle = lngSmaller + ((lngLarger - lngSmaller) / 2)
' Based on the case of StrComp,
' Move either the smaller or larger index to the middle
' Until middle locates on a match
Select Case StrComp(Pair, mstrPairsArray(lngMiddle))
Case -1 'Pair < Array middle
' Adjust the block boundaries
lngLarger = lngMiddle - 1
ndxAttempts = ndxAttempts + 1
'Debug.Print ndxAttempts;
Case 0 ' a match
lngSmaller = lngLarger 'satisfies loop
PairExists = True
Case 1 'Pair > Array middle
' Adjust the block boundaries
lngSmaller = lngMiddle + 1
ndxAttempts = ndxAttempts + 1
'Debug.Print ndxAttempts;
End Select
Loop
'Debug.Print String(ndxAttempts, "-") & "+"
Trap:
End Function
Public Function DataExtractAtoms(Optional ByVal TopicName As String = "A1") As Boolean
Dim ndx As Long
Dim lngCount As Long
Dim strSplitPairs() As String 'array
Dim lngLength As Long
Dim strAtom As String
Dim strPairs As String
Dim intExtractions As Integer
On Error Resume Next
mintMickey = Screen.MousePointer
Screen.MousePointer = vbHourglass
RaiseEvent CommandResponse("Extracting unique members from Paired.txt...")
DoEvents
' Clear Atoms buffer and copy the Pairs to
' strPairs for conditioning
mstrAtomsStringBuffer = ""
strPairs = mstrPairsStringBuffer
' Flatten the inner and outer delimiters to the same (Replace "**" with CRLF )
strPairs = Replace(strPairs, "**", vbCrLf)
' Split the delimited members to an array
strSplitPairs = Split(strPairs, vbCrLf)
' Loop through array, checking for new Members from Pairs
lngCount = UBound(strSplitPairs)
For ndx = 0 To lngCount
' Test that the array element is in the Atoms buffer
If InStr(1, mstrAtomsStringBuffer, strSplitPairs(ndx)) = 0 Then 'not there
' Concatenate the new randomized atom to the buffer at the beginning
mstrAtomsStringBuffer = strSplitPairs(ndx) & vbTab & CStr(Int(Rnd * 360) + 1) & vbCrLf & mstrAtomsStringBuffer
intExtractions = intExtractions + 1
End If
If ndx Mod 100 = 0 Then
RaiseEvent Progress("Extracting atoms... " & CStr(ndx))
DoEvents
End If
Next
' Remove the trailing delimiter
mstrAtomsStringBuffer = Left$(mstrAtomsStringBuffer, Len(mstrAtomsStringBuffer) - 1)
' The reduced set of Pairs is now a unique set of delimited Atoms in mstrAtomsStringBuffer.
' Write the fresh Members data to file as the Members of the top Topic named
' the value in 'TopicName' in a folder named by the value in 'TopicName'
With New clsFile
' Create a new top distribution if non-existant,
' or query the user for overwrite permission,
' else bail, no persistence -- no existence
Select Case .FileExists(Root.WhsePath & "\" & TopicName & "\Members.txt")
Case True
If MsgBox("Overwrite the Members.txt file for Topic " & TopicName & "?", vbYesNo + vbQuestion + vbDefaultButton2, "Random distribution available") = vbYes Then
If .Save(mstrAtomsStringBuffer, UseAppend:=False) Then
' Create (or err past if existing) a folder for the top node, named A1
DataExtractAtoms = True
Else
RaiseEvent CommandResponse("Unable to save the Atoms extracted from Paired.txt to " & .Filespec)
MsgBox "Unable to save the Atoms extracted from Paired.txt to: " & .Filespec
End If
End If
Case False
' Create the new Topic Folder
MkDir Root.WhsePath & "\" & TopicName
If .Save(mstrAtomsStringBuffer) Then
RaiseEvent CommandResponse("Extracted Atoms saved to " & .Filespec)
DataExtractAtoms = True
Else
RaiseEvent CommandResponse("Unable to save the Atoms extracted from Paired.txt to: " & .Filespec)
MsgBox "Unable to save the Atoms extracted from Paired.txt to: " & .Filespec
End If
End Select
End With
RaiseEvent CommandResponse("Atoms extracted: " & CStr(intExtractions))
Screen.MousePointer = mintMickey
End Function
Public Function DataExtractSelectedAtoms(ByVal TopicName As String) As Boolean
' On the selected topic,
' Tabulate a string concatenation of Atom names and RadialAngles
' between starting and ending RadialSelection degrees
' and save them into a a new folder created,
' named by the value of TopicName
' Format: AtomCategoryValue vbTab Degree
' where Degree is 1 to 360.
Dim intCount As Integer
Dim strRadialSelection As String
Dim strSplitPairs() As String 'array
Dim lngLength As Long
Dim atomX As clsAtom
On Error Resume Next
mintMickey = Screen.MousePointer
Screen.MousePointer = vbHourglass
RaiseEvent CommandResponse("Extracting selected member atoms from " & Context.TopicSelection.Name)
' Loop through all atoms of the context selected
' and string concatenate a tabular file stream
With Context.TopicSelection
For Each atomX In .Atoms
If atomX.Degree >= RadialSelectionStart Then
If atomX.Degree <= .Meta.SelectEnd Then
strRadialSelection = strRadialSelection & atomX.Name & vbTab & CStr(atomX.Degree) & vbCrLf
intCount = intCount + 1
End If
End If
Next
End With 'Context.TopicSelection
' Remove the trailing delimiter
strRadialSelection = Left$(strRadialSelection, Len(strRadialSelection) - Len(vbCrLf))
' Write the fresh selection Members data to file as the Members of the new Topic named
' the value in 'TopicName' in a folder named by the value in 'TopicName'
With New clsFile
' Create a new Members.txt file if non-existant,
' or query the user for overwrite permission,
' else bail, no persistence -- no existence
Select Case .FileExists(Root.WhsePath & Context.TopicSelection.Path & "\" & TopicName & "\Members.txt")
Case True
If MsgBox("Overwrite the Members.txt file for Topic " & TopicName & "?", vbYesNo + vbQuestion + vbDefaultButton2, "Random distribution available") = vbYes Then
If .Save(strRadialSelection, UseAppend:=False) Then
' Create (or err past if existing) a folder for the top node, named A1
DataExtractSelectedAtoms = True
Else
MsgBox "Unable to save the Atoms Selected to: " & .Filespec
End If
End If
Case False
' Create the new Topic Folder
MkDir Root.WhsePath & Context.TopicSelection.Path & "\" & TopicName 'errors if exists
If Not .Save(strRadialSelection) Then 'any sort of error will cause the next line to execute by default of resume-next
RaiseEvent CommandResponse("Unable to save the extractted members to: " & .Filespec)
MsgBox "Unable to save the Atoms extracted from Paired.txt to: " & .Filespec
Else
DataExtractSelectedAtoms = True
End If
End Select
End With
RaiseEvent CommandResponse("Atom 'Extraction' successful for selected radial-members as " & TopicName & ". " & CStr(intCount) & " radial-members extracted.")
Screen.MousePointer = mintMickey
End Function
Public Function DataExtractResidualAtomsBuggy(Optional ByVal ResidualTopicName As String = "R") As Boolean
' On the selected topic,
' Tabulate a string concatenation of Atom names and RadialAngle
' that are not found in a child,
' and save them into a new folder created,
' named by the value of ResidualTopicName
Dim ndx, ndxDeepest As Long
Dim strResidual As String
Dim strAtoms() As String 'array
Dim lngLength As Long
Dim topicX As clsTopic
Dim atomX As clsAtom
Dim strTemp As String
On Error Resume Next
' Dimension a storage array to hold all of the atoms of all of the child topics
' This number will be no bigger than the atom count of the topic.
ReDim strAtoms(Context.TopicSelection.Atoms.Count)
' Loop through all atoms of all children of the selected context
' and string concatenate a tabular file stream
With Context.TopicSelection
For Each topicX In .Structure.Children
For Each atomX In topicX.Atoms
strAtoms(ndx) = atomX.Name
ndx = ndx + 1
'Debug.Print topicX.Name; ndx
Next
Next
' Redim the atoms array to loose any trailing unused array elements
ReDim Preserve strAtoms(ndx - 1)
ndx = 0
' Sort the Array into ordinal rank predicated by string compare method
Do While ndx < UBound(strAtoms) - 1
Select Case StrComp(strAtoms(ndx), strAtoms(ndx + 1))
Case -1, 0 ' ndx1 <= ndx2
ndx = ndxDeepest + 1
ndxDeepest = ndx
Case 1 'ndx1 > ndx2
' Swap the inordinate pair
strTemp = strAtoms(ndx + 1)
strAtoms(ndx + 1) = strAtoms(ndx)
strAtoms(ndx) = strTemp
' Store the deepest progress
If ndx > ndxDeepest Then ndxDeepest = ndx
' Backup the index by one
' to reinspect the prior pair,
' unless its the top pair
If ndx > 0 Then ndx = ndx - 1
End Select
'Debug.Print CStr(ndx) & String(ndx + 1, "-") & "+"
Loop
Dim ndx2 As Integer
ndx = 0
For Each atomX In .Atoms
' Concatenate a file stream tabulation
' of all atoms that are not in the strAtoms array
If Not ElementExists(strAtoms, atomX.Name) Then
ndx = ndx + 1
strResidual = strResidual & atomX.Name & vbTab & CStr(atomX.Degree) & vbCrLf
Else
ndx2 = ndx2 + 1
End If
Next
'Debug.Print ndx; ndx2
' Remove the trailing delimiter
strResidual = Left$(strResidual, Len(strResidual) - Len(vbCrLf))
End With 'Context.TopicSelection
' Write the fresh selection Members data to file as the Members of the new Topic named
' the value in 'ResidualTopicName' in a folder named by the value in 'ResidualTopicName'
With New clsFile
' Create a new Members.txt file if non-existant,
' or query the user for overwrite permission,
' else bail, no persistence -- no existence
Select Case .FileExists(Root.WhsePath & Context.TopicSelection.Path & "\" & ResidualTopicName & "\Members.txt")
Case True
If MsgBox("Overwrite the Members.txt file for Topic " & ResidualTopicName & "?", vbYesNo + vbQuestion + vbDefaultButton2, "Random distribution available") = vbYes Then
If .Save(strResidual, UseAppend:=False) Then
' Create (or err past if existing) a folder for the top node, named A1
DataExtractResidualAtomsBuggy = True
Else
MsgBox "Unable to save the Atoms Selected to: " & .Filespec
End If
End If
Case False
' Create the new Topic Folder
MkDir Root.WhsePath & Context.TopicSelection.Path & "\" & ResidualTopicName 'errors if exists
If Not .Save(strResidual) Then 'any sort of error will cause the next line to execute by default of resume-next
MsgBox "Unable to save the Atoms extracted from Paired.txt to: " & .Filespec
Else
DataExtractResidualAtomsBuggy = True
End If
End Select
End With
Set atomX = Nothing
Set topicX = Nothing
End Function
Public Function DataExtractResidualAtoms(Optional ByVal ResidualTopicName As String = "R") As Boolean
' On the selected topic,
' Tabulate a string concatenation of Atom names and RadialAngle
' that are not found in a child,
' and save them into a new folder created,
' named by the value of ResidualTopicName
Dim ndx, ndx2, ndxDeepest As Long
Dim strResidual As String
Dim strSiblingAtoms As String
Dim lngLength As Long
Dim topicX As clsTopic
Dim atomX As clsAtom
Dim strTemp As String
On Error Resume Next
mintMickey = Screen.MousePointer
Screen.MousePointer = vbHourglass
RaiseEvent CommandResponse("Extracting residual member atoms from " & Context.TopicSelection.Name)
' Loop through all atoms of all children of the selected context
' and string concatenate a sibling atom delimitation
With Context.TopicSelection
For Each topicX In .Structure.Children
For Each atomX In topicX.Atoms
strSiblingAtoms = strSiblingAtoms & "|" & atomX.Name
ndx = ndx + 1
Next
Next
'Debug.Print "Total Atoms: " & CStr(ndx)
For Each atomX In .Atoms
' Concatenate a file stream tabulation
' of all atoms that are not in the strSiblingAtoms array
'Debug.Print InStr(1, strSiblingAtoms, atomX.Name)
If InStr(1, strSiblingAtoms, atomX.Name) = 0 Then 'residual found
ndx = ndx + 1
strResidual = strResidual & atomX.Name & vbTab & CStr(atomX.Degree) & vbCrLf
Else
ndx2 = ndx2 + 1
End If
Next
'Debug.Print ndx; ndx2
' Remove the trailing delimiter
strResidual = Left$(strResidual, Len(strResidual) - Len(vbCrLf))
End With 'Context.TopicSelection
' Write the fresh selection Members data to file as the Members of the new Topic named
' the value in 'ResidualTopicName' in a folder named by the value in 'ResidualTopicName'
With New clsFile
' Create a new Members.txt file if non-existant,
' or query the user for overwrite permission,
' else bail, no persistence -- no existence
Select Case .FileExists(Root.WhsePath & Context.TopicSelection.Path & "\" & ResidualTopicName & "\Members.txt")
Case True
If MsgBox("Overwrite the Members.txt file for Topic " & ResidualTopicName & "?", vbYesNo + vbQuestion + vbDefaultButton2, "Random distribution available") = vbYes Then
If .Save(strResidual, UseAppend:=False) Then
' Create (or err past if existing) a folder for the top node, named A1
DataExtractResidualAtoms = True
Else
RaiseEvent CommandResponse("Unable to save the residual members to: " & .Filespec)
MsgBox "Unable to save the residual members to: " & .Filespec
End If
End If
Case False
' Create the new Topic Folder
MkDir Root.WhsePath & Context.TopicSelection.Path & "\" & ResidualTopicName 'errors if exists
If Not .Save(strResidual) Then 'any sort of error will cause the next line to execute by default of resume-next
RaiseEvent CommandResponse("Unable to save the residual members to: " & .Filespec)
MsgBox "Unable to save the residual members to: " & .Filespec
Else
DataExtractResidualAtoms = True
End If
End Select
End With
RaiseEvent CommandResponse(CStr(ndx) & " residual members of topic " & Context.TopicSelection.Name & " extracted to topic " & ResidualTopicName)
Screen.MousePointer = mintMickey
Set atomX = Nothing
Set topicX = Nothing
End Function
Function ElementExists(ByRef SourceArray As Variant, ByVal Element As String) As Boolean
' Element is compared against element in the middle,
' then successive binary division of remaining middles
' on either left or right of the compare point,
' until the exact match is found, which returns true,
' or until there are no further element locations to divide, returning false
' The SourceArray is assumed to be sorted in ascending or descending rank
' The ordination of rank among the array names then become a
' self-index into the array, based on a successive binary division
' of left or right halves, pending a string comparison returning
' a three-state result, less, equal, greater.
Dim lngSmaller As Long
Dim lngMiddle As Long
Dim lngLarger As Long
Dim ndxAttempts As Integer
On Error Resume Next
lngSmaller = 0
lngLarger = UBound(SourceArray)
'Debug.Print "Total Pairs: " & UBound(SourceArray) + 1
Do While lngSmaller < lngLarger
' Place a middle ndx in the current bounded block
lngMiddle = lngSmaller + ((lngLarger - lngSmaller) / 2)
' Based on the case of StrComp,
' Move either the smaller or larger index to the middle
' Until middle locates on a match
Select Case StrComp(Element, SourceArray(lngMiddle))
Case -1 'Element < Array middle
' Adjust the block boundaries
lngLarger = lngMiddle - 1
ndxAttempts = ndxAttempts + 1
'Debug.Print ndxAttempts;
Case 0 ' a match
lngSmaller = lngLarger 'satisfies loop
ElementExists = True
Case 1 'Element > Array middle
' Adjust the block boundaries
lngSmaller = lngMiddle + 1
ndxAttempts = ndxAttempts + 1
'Debug.Print ndxAttempts;
End Select
Loop
'Debug.Print String(ndxAttempts, "-") & "*"
Trap:
End Function
Public Function DataExtractAtomsSlow() As Boolean
' Seven times slower
Dim oFile As clsFile
Dim ndx As Long
Dim lngCount As Long
Dim lngLength As Long
Dim strAtom As String
Dim strTemp As String
Const cDelimiter = vbCrLf
' Forward-reductively iterate through all delimited Pairs
' For each Atom, remove all subsequent instances of the Atom.
' The number of reduction-iterations will exactly-maybe equal the number of unique Atoms - 1.
On Error GoTo Trap
' Clear Atoms buffer and copy the Pairs to
' strTemp for reduction searches
mstrAtomsStringBuffer = ""
strTemp = mstrAtomsStringBuffer & cDelimiter ' append cDelimiter to the end of the strTemp,
' enabling pattern matching using the cDelimiter
' including the last atom in the strTemp string
' Flatten the delimiters to one
' Replace CRLF and "**" with cDelimiter
strTemp = Replace(strTemp, "**", cDelimiter)
strTemp = Replace(strTemp, vbCrLf, cDelimiter)
'Debug.Print Left$(strTemp, 120)
' Loop on shrinking length
While Len(strTemp) > 2
DoEvents 'yielding to other apps some time
' Locate first Atom
ndx = InStr(1, strTemp, cDelimiter) 'point to 1st cDelimiter
Select Case ndx
Case 0
Case Is > 0
' Concatenate the atom and delimiter onto the class field
strAtom = Left$(strTemp, ndx - 1)
mstrAtomsStringBuffer = strAtom & cDelimiter
' Remove all occurances of the Atom and trailing cDelimiter from strTemp
strTemp = Replace(strTemp, strAtom & cDelimiter, "")
End Select
lngCount = lngCount + 1
'Debug.Print lngCount
Wend
' The reduced set of Pairs is now a unique set of delimited Atoms in mstrAtomsStringBuffer.
DataExtractAtomsSlow = True
ExitPoint:
Exit Function
Trap:
'Append contextual information into existing err object Description
Err.Description = "SLIP.clsSLIP.DataExtractAtoms: " & Err.Description
Err.Raise Err.Number
Resume ExitPoint
End Function
Public Function DataImportPairs(ByVal Filespec As String) As Boolean
' Pairs.txt will always be expected in the DataPath
' Clear existing Document (object model)
' Load the Pairs into the root buffer
' Split the buffer into the PairsArray
Dim ndx As Long
Dim varX As Variant
Dim strTemp As String
On Error Resume Next
mintMickey = Screen.MousePointer
Screen.MousePointer = vbHourglass
RaiseEvent Progress("Importing pair data from " & mstrDataPath & "\Paired.txt...")
DoEvents
Filespec = mstrDataPath & "\Paired.txt"
' Pairs.txt will always be expected in the DataPath
With New clsFile
Select Case .FileExists(Filespec)
Case True
' Load the Pairs into the root buffer
With New clsFile
mstrPairsStringBuffer = modMain.TrimTrailingCharacters(.ReadAll(Filespec), vbCrLf)
End With
' Parse the PairNames into the class-level string array (module scope)
mstrPairsArray = Split(Trim(mstrPairsStringBuffer), vbCrLf, compare:=vbBinaryCompare)
Case False
MsgBox "Pairs file not found: " & Filespec & vbCrLf & vbCrLf & " Import aborted.", vbInformation
End Select
End With 'New clsFile
' Return success
DataImportPairs = True
mbooPairsImported = True
RaiseEvent CommandResponse("Pairs Imported: " & CStr(UBound(mstrPairsArray) + 1) & " from " & Filespec)
RaiseEvent Progress("")
Screen.MousePointer = mintMickey
Exit Function
Trap:
RaiseEvent CommandResponse("Failure importing pair data from " & Filespec)
End Function
Public Property Let DataIterationEnabled(ByVal Value As Boolean)
mbooIterating = Value
RaiseEvent CommandResponse("Iteration " & IIf(mbooIterating, "enabled", "disabled"))
End Property
Public Property Get DataIterationEnabled() As Boolean
DataIterationEnabled = mbooIterating
End Property
Public Function DataLoadMembers(Optional ByVal MembersFileSpec As String) As clsAtoms
Dim strSplit() As String
Dim ndx As Integer
On Error Resume Next
mintMickey = Screen.MousePointer
Screen.MousePointer = vbHourglass
With New clsFile
.Filespec = MembersFileSpec
' Deserialize and return the file contents as a clsAtoms instance
Set DataLoadMembers = Tabular.DeserializeMembers(.ReadAll) 'returns clsAtoms
End With
Screen.MousePointer = mintMickey
End Function
Public Sub DataLoadNestedTopics(ByVal Name As String, Optional ByRef oParent As clsTopic)
' Construct a Topic and for every sub-folder found, make the topic name the folder name
' Construct a subtopic by recursing this function, passing a topic reference as a parent
' with a the folder name appended to the TopicPath
' Note: this function calls itself for recursing subfolders.
Dim topicX As clsTopic 'stores reference to new Topic node instance
Dim intLevelDepth ' For each recursive entrance in this procedure, this stores the tree-depth
Dim strDir As String
Dim strTopicFolder As String
Dim strFolderItem As String
Dim strSubFolders As String 'delimited concatenation
Dim SplitSubFolders() As String 'array
Dim varX As Variant 'for-each enumerator index
On Error Resume Next
' Determine depth into tree by counting ancestors
Select Case (oParent Is Nothing) ' Boolean test
Case True ' Test reveals a root topic, the top Topic instance, not having a parent
' Reset the LevelCounts for a top node
ReDim mintLevelCount(0)
mintLevelCount(0) = 1 'the top node will always be just one
intLevelDepth = 0
' Create a top Topic class instance
' Note: the Topic instance creates all folders and files.
Set topicX = Construct.CreateTopTopic( _
Name, _
cDefPlotTopX, _
cDefPlotTopY)
Case False ' a non-root topic, having a parent
intLevelDepth = UBound(Split(oParent.Path, "\"))
ReDim Preserve mintLevelCount(intLevelDepth + 1)
' Increment a topic graph node count at this level
mintLevelCount(intLevelDepth) = mintLevelCount(intLevelDepth) + 1
' Create, set properties and set the parent of a new Topic
Set topicX = Construct.CreateTopic( _
Name:=Name, _
oParent:=oParent, _
x:=mintLevelCount(intLevelDepth) * 500, _
Y:=oParent.MetricsY + 700)
End Select
' Enumerate all folders in the topic folder, and create a child Topic
' for each folder found by recursing to this function
' with the topicX parameter as oParent
' Build a string holding the path to the topic folder
strTopicFolder = mstrDataPath & topicX.Path & "\" ' Set the path.
' Read the folder for subfolders
strFolderItem = Dir(strTopicFolder, vbDirectory) ' Retrieve the first entry.
Dim x As String
' Concatenate a tabular list of sub folders in the Topic folder, pipe delimited
Do While strFolderItem <> "" ' Start the loop.
' Ignore the current directory and the encompassing directory.
If strFolderItem <> "." And strFolderItem <> ".." Then
' Use bit-masking to test for a directory.
If (GetAttr(strTopicFolder & strFolderItem) And vbDirectory) = vbDirectory Then
' Concatenate the folder name with a pipe
strSubFolders = strSubFolders & strFolderItem & "|"
End If
End If
strFolderItem = Dir ' Get next entry.
'Debug.Print strFolderItem, strSubFolders
Loop
' Remove trailing delimiter
If Len(strSubFolders) > 0 Then
strSubFolders = Left$(strSubFolders, Len(strSubFolders) - 1)
End If
' Enumerate the tabulation of folders, and create a sub-folder for each
SplitSubFolders = Split(strSubFolders, "|")
'Debug.Print topicX.Path
For Each varX In Split(strSubFolders, "|")
' Recursion happens here
' Create a child Topic on topicX with the folder name in strFolderItem
' not working when nested...Dir statement can't terminate by ever returning null.
DataLoadNestedTopics varX, topicX
Next
End Sub
Public Sub DataRandomizeAtoms(ByRef oTopic As clsTopic, ByVal MinMax As Long)
' Random spatial assignment within a cubical volume (X,Y,Z).
Dim atomX As clsAtom
' Reset iteration count
oTopic.RadialIters = 0
For Each atomX In oTopic.Atoms
' Radial assignment
atomX.Degree = CInt(Rnd * 360) + 1
' Random spatial assignment within a cubical volume (X,Y,Z).
atomX.MetricsX = CInt(Rnd * MinMax) - MinMax / 2 + 400
atomX.MetricsY = CInt(Rnd * MinMax) - MinMax / 2 + 400
atomX.MetricsZ = CInt(Rnd * MinMax) - MinMax / 2 + 400
Next
' Save the Members tabulation to file
With New clsFile
' Convert the Atoms object to a string
' Save the tabulation of the current iterated state of members
.Save _
Buffer:=Tabular.SerializeMembers(oTopic.Atoms), _
Filespec:=mstrDataPath & oTopic.Path & "\Members.txt"
.CloseFileWrite
' Send notifacation message
RaiseEvent AtomRadialDistributionChanged(oTopic)
End With
End Sub
Public Function DataSaveMembers(ByRef oAtoms As clsAtoms) As Boolean
On Error GoTo Trap
With New clsFile
.Save DataAtomsStringBuffer, mstrDataPath & "\Members.txt"
End With
DataSaveMembers = True
Exit Function
Trap:
End Function
Public Function MetricsAtomicDistance(AtomA As clsAtom, AtomB As clsAtom) As Long
MetricsAtomicDistance = Abs(( _
(AtomB.MetricsX - AtomA.MetricsX) ^ 3 + _
(AtomB.MetricsY - AtomA.MetricsY) ^ 3 + _
(AtomB.MetricsZ - AtomA.MetricsZ) ^ 3 _
) ^ (1 / 3))
End Function
Public Function MetricsTopicDistance(Topic1 As clsTopic, Topic2 As clsTopic) As Long
MetricsTopicDistance = Abs(( _
(Topic2.MetricsX - Topic1.MetricsX) ^ 3 + _
(Topic2.MetricsY - Topic1.MetricsY) ^ 3 + _
(Topic2.MetricsZ - Topic1.MetricsZ) ^ 3 _
) ^ (1 / 3))
End Function
Public Property Get metricsLabelWidth() As Long
metricsLabelWidth = 2000
End Property
Public Property Get metricsPlotSize() As Long
metricsPlotSize = 4695
End Property
Public Property Get MetricsRadius() As Long
MetricsRadius = 1000
End Property
Public Property Get metricsTopX() As Long
metricsTopX = 500
End Property
Public Property Get metricsTopY() As Long
metricsTopY = 500
End Property
Public Property Get metricsWhiteSpace() As Long
metricsWhiteSpace = 60
End Property
Public Function TabularDeserializeMembers(ByVal Members As String) As clsAtoms
' Transform a serialized string to class instance
Dim strOuterSplit() As String 'array
Dim strInnerSplit() As String 'array
Dim strAtom As String
Dim oAtom As clsAtom
Dim oMembers As clsAtoms
Dim ndx, lastNdx As Integer
Const cOuterDelimiter = vbCrLf
On Error Resume Next
' Split the members string into an array
strOuterSplit = Split(Members, cOuterDelimiter)
' Store the index of the last array element
lastNdx = UBound(strOuterSplit)
' Instance an Atoms class, the return object
Set oMembers = New clsAtoms
' Enumerate the split array
For ndx = 0 To lastNdx
' Instance a new atom
With New clsAtom
strInnerSplit = Split(strOuterSplit(ndx), vbTab)
If UBound(strInnerSplit) = 1 Then '
.Name = strInnerSplit(0) 'left of inner delimiter
.Degree = CInt(strInnerSplit(1)) 'right of inner delimiter
' Add a new reference in the collection class of the parent object
If Len(.Name) Then
oMembers.VBACollection.Add Item:=.Self, Key:=.Name
End If
End If
End With
Next
Set TabularDeserializeMembers = oMembers
Set oMembers = Nothing
Set oAtom = Nothing
End Function
Public Function TabularMembersStringToRadialsString(ByVal Members As String) As String
' Input: Name Tab Degree CR
' Output: RadialAngle Tab Count CR
' The output contains 360 Radial counts, or sum of members per degree
Dim intDegreeCounts(360) As Integer 'ignore zero base element
Dim strOuterSplit() As String 'array
Dim intDegree As Integer
Dim ndx As Integer
Dim intLast As Integer
Const cOuterDelimiter = vbCrLf
Const cInnerDelimiter = vbTab
On Error Resume Next
' Split the members string into an array
strOuterSplit = Split(Members, cOuterDelimiter)
' Store the last array element index
intLast = UBound(strOuterSplit)
' Enumerate the split array summing counts of Members per degree
For ndx = 0 To intLast
' Get the degree by splitting the member, and accessing the 2nd element (1)
intDegree = CInt(Split(strOuterSplit(ndx), cInnerDelimiter)(1)) 'left element of split on outer split, the member degree
' Sum the count per degree
intDegreeCounts(intDegree) = intDegreeCounts(intDegree) + 1
Next
' Concatenate the output text - RadialAngle Tab Count CR
For ndx = 1 To 360
TabularMembersStringToRadialsString = TabularMembersStringToRadialsString & _
CStr(ndx) & vbTab & CStr(intDegreeCounts(ndx)) & vbCrLf
Next
End Function
Public Function TabularSerializeMembers(ByRef Atoms As clsAtoms) As String
Dim atomX As clsAtom
Dim ndx As Integer
On Error Resume Next
' Concatenate Members as - Name Tab Degree CR
For Each atomX In Atoms
TabularSerializeMembers = TabularSerializeMembers & atomX.Name & vbTab & CStr(atomX.Degree) & vbCrLf
Next
End Function
Public Function TabularSerializeRadials(ByRef Atoms As clsAtoms) As String
Dim atomX As clsAtom
Dim intDegreeCounts(360) As Integer 'ignore zero base element
Dim ndx As Integer
On Error Resume Next
' Sum count of Atoms per degree
For Each atomX In Atoms
intDegreeCounts(atomX.Degree) = intDegreeCounts(atomX.Degree) + 1
Next
' Concatenate Degree Tab Count CR
For ndx = 1 To 360
TabularSerializeRadials = _
TabularSerializeRadials & CStr(ndx) & vbTab & CStr(intDegreeCounts(ndx)) & vbCrLf
Next
End Function
Public Property Get DataPath() As String
If Len(DataPath) Then mstrDataPath = DataPath Else DataPath = cDefDataPath
End Property
Public Property Let DataPath(ByVal Value As String)
mstrDataPath = Value
End Property
Public Property Get EventBrowserExecName() As String
EventBrowserExecName = mstrEventBrowserExecName
End Property
Public Property Let EventBrowserExecName(ByVal Value As String)
mstrEventBrowserExecName = Value
End Property
Public Property Get TopicSelection() As clsTopic
On Error Resume Next
Set TopicSelection = moTopicSelection
End Property
Public Property Set TopicSelection(ByVal ref As clsTopic)
On Error Resume Next
Set moTopicSelection = ref
End Property
Public Property Get TopicCount() As Long
TopicCount = mcolTopicTree.Count
End Property