McUserMeasure Interface |
![]() |
Namespace: MediaCy.IQL.Features
Public Interface McUserMeasure Inherits IMcUserMeasure
VERSION 5.00 Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} EventBasedUserMeasure Caption = "Event Based User Measurement" ClientHeight = 2280 ClientLeft = 45 ClientTop = 330 ClientWidth = 4710 OleObjectBlob = "EventBasedUserMeasure.frx":0000 ShowModal = 0 'False StartUpPosition = 1 'CenterOwner End '**** EventBasedUserMeasure.frm **** 'This form illustrates how a user-defined measurement may be implemented with 'event handing. The measurement defined here, mRgnUserEventsCircularity, will be 'available as a measurment on any McRegions object as long as this form is open. ' ' Note: Circularity as computed here is just the inverse of the built-in mRgnRoundness measurement. ' 'WARNING: the events-base approach to implementing a user-defined measurement 'that is illustrated here is not recommended except in very unusual 'circumstances. Instead, use a CLS module that implements IMcUserMeasure. The 'CLS-based approach is much faster, more transparent, can live without a form and 'is not much, if any, more work to implement. See, for example, 'CUserMeasCircularityMinCode or CUserMeasCircularityFull which implement the same 'measurement defined here using a CLS module. CUserMeasCircularityMinCode 'and CUserMeasCircularityFull are registered in the 'RegisterExampleUserMeasurements sub found in "McUserMeasure_Examples.bas". Option Explicit Dim WithEvents mywitheventsCircularityUserMeasure As McUserMeasure Attribute mywitheventsCircularityUserMeasure.VB_VarHelpID = -1 Private Const strMeasureName As String = "mRgnUserEventsCircularity" '***** User measurement supported via events ***** Private Sub mywitheventsCircularityUserMeasure_GetDescription(ByVal MeasurementObject As IMcMeasure, Description As String) Description = "Event Version:" + vbCrLf + "Circularity is (Area / Perimeter^2) * 4 * PI." End Sub Private Sub mywitheventsCircularityUserMeasure_ComputeValue(ByVal MeasurementObject As IMcMeasure, _ ByVal ParentOperator As Object, vValue As Variant) Dim myRegions As McRegions Set myRegions = ParentOperator Dim mcobjVal As McObject Set mcobjVal = _ McOpMul(McOpDiv(myRegions.mRgnPolygonialArea, McPow(myRegions.mRgnPerimeter, 2)), 4# * 3.1416) 'Set results where perimeter or area is zero to missing mcobjVal.SelectedValues(McOpOR(McOpEQ(myRegions.mRgnPerimeter, 0#), McOpEQ(myRegions.mRgnPolygonialArea, 0#))) = McMissingDouble Set vValue = mcobjVal 'return the McObject itself 'vValue = mcobjVal 'this also works, but is less efficient End Sub 'ComputeValue event '***** Measurement registration ***** Sub RegisterTheMeasurement() 'Only register the measurement if it is not already registered If GetRegisteredUserMeasurement(strMeasureName) Is Nothing Then Set mywitheventsCircularityUserMeasure = New McUserMeasure RegisterUserMeasurement strMeasureName, mywitheventsCircularityUserMeasure, mcmpaChildOfRegions End If ' need to register End Sub 'RegisterTheMeasurement Sub UnRegisterTheMeasurement() RegisterUserMeasurement strMeasureName, Nothing 'unregister our measurement Set mywitheventsCircularityUserMeasure = Nothing 'and we are done with our event handling End Sub 'UnRegisterTheMeasurement '***** Form Load/Unload ***** Private Sub UserForm_Initialize() RegisterTheMeasurement End Sub Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) UnRegisterTheMeasurement End Sub '***** Buttons on the form **** Private Sub OpenImageCB_Click() Images.Open Path + "Images\BoneBig.jpg" 'find some regions ActiveImage.RegionFeatures.Threshold.Execute End Sub 'OpenImageCB_Click Private Sub ShowMeasurementCB_Click() If ActiveImage Is Nothing Then Results.Text = "No active image. Open one and make some RegionFeatures." Exit Sub End If 'Here, we show some measurement results RegisterTheMeasurement 'in case someone unregistered us, re-register Dim myM As McMeasure Set myM = ActiveImage.RegionFeatures.mRgnUserEventsCircularity 'The below also works Set myM = ActiveImage.RegionFeatures.Measures(strMeasureName) Results.Text = myM.Description Results.Text = Results.Text + vbCrLf + "On " + Str(myM.Count) + " selected features." _ + vbCrLf + "Mean: " + Str(myM.Mean) + vbCrLf _ + "Min: " + Str(myM.Min) + vbCrLf _ + "Max: " + Str(myM.Max) End Sub 'ShowMeasurementCB_Click
'**** McUserMeasure_Examples.bas **** 'This module shows how to register and use a variety of example user-defined measurements. 'The example measurements range from bare-bones to full-featured. Standard, 'per-feature measurements are illustrated as well as non-standard measurements. 'To see how to automatically register user-defined measurements at startup, look at the 'code in the "ThisProject" and "ThisSession" IQStudio Objects. 'See the help on the McMeasure class for a discussion of measurement naming conventions. ' 'Each measurement is implemented in a CLS module as described below: 'CUserMeasureTemplate does not actually implement a measurement, but is a template 'CLS file that you can modify to implement your own measurement. 'CUserMeasureCircularityMinCode implements a standard per-feature measurement of 'region circularity with only the absolute minimum necessary implementation code. 'This measurement is registered as a McRegions measurement named '"mRgnUserCircularityMinCode" by the RegisterExampleUserMeasurements sub, below. 'CUserMeasCircularityFull implements a standard per-feature measurement of 'region circularity with everything necessary to create a new measurement 'indistiguishable from those built into the application. This includes a 'localized Display Name and Description, as well as an illustration. 'This measurement is registered as a McRegions measurement named '"mRgnUserCircularityFull" by the RegisterExampleUserMeasurements sub, below. 'CUserMeasureBoundingRect implements a measurement that has one fixed length array value per 'feature in the ancestor McFeatures object. The measurement reports the 'bounding rect of each feature as a length-4 array of 'Double in the order Left, Top, Right, Bottom. The results are 'in calibrated coordinates. 'This measurement is registered by the RegisterExampleUserMeasurements sub below 'under three names: as a McRegions measurement named '"maRgnUserBoundingRect", as a McLines measurement named "maLnUserBoundingRect" 'and as a McRefFeatures measurement named "maRefUserBoundingRect" 'CUserMeasureCoordinates implements a measurement that results in a variable 'number of values per feature in the ancestor McFeatures object. The measurement reports the 'coordinate points each feature as a length-N array of 2 Single X and Y values. 'The results are in calibrated coordinates. 'This measurement is registered by the RegisterExampleUserMeasurements sub below 'under two names to work as an immediate child of a McLines or McRegions 'with names mvLnUserCoordinates, mvRgnUserCoordinates, respectively. 'CUserMeasureConvexHull implements a measurement that returns a McRegions result. 'In this case, the result McRegions features are the convex hulls 'of the ancestor McRegions features. The result McRegions is created 'as a child of the measured ancestor McRegions. 'This measurement is registered by the RegisterExampleUserMeasurements sub below 'as mrRgnUserConvexHull to work as an immediate child of a McRegions object. 'Note that this measurement is the same as the built-in mrRgnConvexHull measurement. 'CUserMeasureSortedIndices implements a measurement that illustrates several 'unusual measurement possiblitilits: ' ' 1. The measurement results are based on an ancestor McFeatures, but 'the results are not one result associated with each feature the way that 'most measurements are. ' ' 2. The measurement uses per-McMeasure data to do its work. Remember 'that only one McUserMeasure instance is registered under a given name, 'so this one instance has to service all McMeasure instances that are 'created on legal parent objects (and there may be many such). ' ' 3. The standard, automatically serviced dependencies are not 'adequate for this measurement, so it implements the UserIsValueStale 'method to determine when the measurement value needs to be recomputed. ' ' 4. The measurement results are returned as Long integral values rather than 'as floating point values. 'The measurment result is a 1-dimensional array of feature indices 'showing the sorted order of some per-feature measurement. 'The length of the array is equal to the McFeatures.Count of the ancestor 'McFeatures upon which the measurement is being made, but instead of 'each value corresponding to a feature, it is instead an index of 'the feature that holds that sorted rank order for the measurement being 'sorted (e.g., the feature index with the median sorted-by measurement value 'will be the middle value of this array; see TestExampleUserMeasurements 'below for a couple of examples where this is done). 'The measurement will work with any ancestor McFeatures but it is registered 'under three names to work as an immediate child of a McLines or McRegions, or as 'a descendent of any McFeatures with names miLnUserSortedByLength, miRgnUserSortedByArea 'and miFeatUserSortedByAny, respectively. Option Explicit Public Sub RegisterExampleUserMeasurements() Dim nRegistered As Long nRegistered = 0 'Turn off notifies from Engine, so only one registration notify can be fired. 'We do this because repopulating lists of all available measurements can 'be time consuming, and we only want notify sinks to have to do it once 'for all of our registrations. Dim EngineHasObject As IMcHasObject Set EngineHasObject = Engine Dim bEntryNotifyState As Boolean bEntryNotifyState = EngineHasObject.FirePropertyChangeNotifies EngineHasObject.FirePropertyChangeNotifies = False 'Only register measurements if they are not already registered If GetRegisteredUserMeasurement("mRgnUserCircularityMinCode") Is Nothing Then RegisterUserMeasurement "mRgnUserCircularityMinCode", New CUserMeasureCircularityMinCode, mcmpaChildOfRegions nRegistered = nRegistered + 1 End If ' need to register mRgnUserCircularityMinCode If GetRegisteredUserMeasurement("mRgnUserCircularityFull") Is Nothing Then RegisterUserMeasurement "mRgnUserCircularityFull", New CUserMeasureCircularityFull, mcmpaChildOfRegions nRegistered = nRegistered + 1 End If ' need to register mRgnUserCircularityFull 'The measurement implemented by the CUserMeasureBoundingRect class 'will work with any ancestor McFeatures but it is registered here 'under three names to work as an immediate child of a McLines, McRegions or 'a McRefFeatures with names maLnUserBoundingRect, maRgnUserBoundingRect 'and maRefUserBoundingRect, respectively. If GetRegisteredUserMeasurement("maRgnUserBoundingRect") Is Nothing Then RegisterUserMeasurement "maRgnUserBoundingRect", New CUserMeasureBoundingRect, mcmpaChildOfRegions nRegistered = nRegistered + 1 End If ' need to register maRgnUserBoundingRect If GetRegisteredUserMeasurement("maLnUserBoundingRect") Is Nothing Then RegisterUserMeasurement "maLnUserBoundingRect", New CUserMeasureBoundingRect, mcmpaChildOfLines nRegistered = nRegistered + 1 End If ' need to register maLnUserBoundingRect If GetRegisteredUserMeasurement("maRefUserBoundingRect") Is Nothing Then RegisterUserMeasurement "maRefUserBoundingRect", New CUserMeasureBoundingRect, mcmpaChildOfRefFeatures nRegistered = nRegistered + 1 End If ' need to register maRefUserBoundingRect 'The measurement implemented by the CUserMeasureCoordinates class 'will work with any ancestor McFeatures but it is registered here 'under two names to work as an immediate child of a McLines or McRegions 'with names mvLnUserCoordinates and mvRgnUserCoordinates, respectively. If GetRegisteredUserMeasurement("mvRgnUserCoordinates") Is Nothing Then RegisterUserMeasurement "mvRgnUserCoordinates", New CUserMeasureCoordinates, mcmpaChildOfRegions nRegistered = nRegistered + 1 End If ' need to register mvRgnUserCoordinates If GetRegisteredUserMeasurement("mvLnUserCoordinates") Is Nothing Then RegisterUserMeasurement "mvLnUserCoordinates", New CUserMeasureCoordinates, mcmpaChildOfLines nRegistered = nRegistered + 1 End If ' need to register mvLnUserCoordinates 'The measurement implemented by the CUserMeasureConvexHull class 'will work with any ancestor McRegions but it is registered here 'named mrRgnUserConvexHull to work as an immediate child of a McRegions. If GetRegisteredUserMeasurement("mrRgnUserConvexHull") Is Nothing Then RegisterUserMeasurement "mrRgnUserConvexHull", New CUserMeasureConvexHull, mcmpaChildOfRegions nRegistered = nRegistered + 1 End If ' need to register mrRgnUserConvexHull 'The measurement implemented by the CUserMeasureSortedIndices class will work 'with any ancestor McFeatures and associated sort-by measurement, but it is 'registered here under three names. Two names, "miLnUserSortedByLength" and '"miRgnUserSortedByArea", work as an immediate child of a McLines or McRegions, 'automatically sorting features based on on mLnLength or mRgnArea, respectively. 'The class is also registered as "miFeatUserSortedByAny" to work as a descendent of 'any McFeatures and sort based on a measurement assigned to the UserData property '(see TestExampleUserMeasurements). The CUserMeasureSortedIndices parses the 'registered name to see if it can automatically figure out what measurement to 'sort on. If GetRegisteredUserMeasurement("miLnUserSortedByLength") Is Nothing Then RegisterUserMeasurement "miLnUserSortedByLength", New CUserMeasureSortedIndices, mcmpaChildOfLines nRegistered = nRegistered + 1 End If ' need to register miLnUserSortedByLength If GetRegisteredUserMeasurement("miRgnUserSortedByArea") Is Nothing Then RegisterUserMeasurement "miRgnUserSortedByArea", New CUserMeasureSortedIndices, mcmpaChildOfRegions nRegistered = nRegistered + 1 End If ' need to register miRgnUserSortedByArea If GetRegisteredUserMeasurement("miFeatUserSortedByAny") Is Nothing Then RegisterUserMeasurement "miFeatUserSortedByAny", New CUserMeasureSortedIndices, mcmpaDescendentOfFeatures nRegistered = nRegistered + 1 End If ' need to register miFeatUserSortedByAny 'Restore Engine notifies EngineHasObject.FirePropertyChangeNotifies = bEntryNotifyState If nRegistered <> 0 Then 'were any registered? EventLog.WriteEntry Str(nRegistered) + " user measurements registered.", mceleInformation, 0, mcelcFromUser 'We fire just one "PropertyChanged" notify for all of the registrations EngineHasObject.DoFirePropertyChangeNotify ID_IMcEngine_RegisterUserMeasurement End If 'any were registered End Sub 'RegisterExampleUserMeasurements 'Here is an example of using some of the user measurements registered above Public Sub TestExampleUserMeasurements() 'Register our example measurements, if necessary RegisterExampleUserMeasurements 'Open BoneBig.jpg as a test image Images.Open Path + "Images\BoneBig.jpg" With ActiveImage 'set an AOI .Aoi.SetBox -1, 0, 0, 150, 150 ActiveWindow.View.Magnification = 2# 'and show it bigger ActiveWindow.View.Pan = 0 ActiveWindow.View.Scroll = 0 Output.Show "Output" 'make sure the Output window's "Output" page is showing Output.Clear 'and empty With .RegionFeatures .Threshold.Execute 'create some regions .CleanUpBordersAndNoise , mcrbNoBorder, 200 'get rid of small features Output.PrintMessage "*****" + Str(.Count) + " large region features found." Output.PrintMessage "Circularities = " + McToText( _ .mRgnUserCircularityFull, "%0.4f") Output.PrintMessage "Bounding Rectangles: " + vbCrLf + McToText( _ .maRgnUserBoundingRect, "%.2f") Output.PrintMessage "First 3 coordinate X,Y values of each region feature: " + vbCrLf + McToText( _ .Measures("mvRgnUserCoordinates").ValueMcObject.SelectedMcObject(, Array(0, 1, 2))) 'Note that measurements may be accessed directly by .Name or by .Measures("Name"). 'Both approaches are equally efficient, but the Measures approach has the advantage of 'allowing you to use intellisense on the McMeasure object's properties and methods. 'The disadvantage is that it is not as clean looking and so may be more difficult to 'maintain. 'Show the convex hulls in yellow Dim myConvexHulls As McRegions Set myConvexHulls = .mrRgnUserConvexHull.value myConvexHulls.AutoDisplay = True 'display the convex hulls myConvexHulls.DisplayedObjects.SetColors &HFFFF& 'Show the biggest region and the median circularity in a different color Dim mcobjSortedFeatures As McObject Set mcobjSortedFeatures = .miRgnUserSortedByArea.ValueMcObject Dim lFeatOfInterest As Long 'get feature index with highest mRgnArea lFeatOfInterest = mcobjSortedFeatures.SelectedValues(mcobjSortedFeatures.VectorLength - 1) Dim DisplayedObj As McDisplayedObjects Set DisplayedObj = .DisplayedObjects(lFeatOfInterest) DisplayedObj.SetColors &HFF00FF 'Magenta DisplayedObj.SetBorderWidth 2 DisplayedObj.SetLabelText "Biggest" 'get feature index with the median mRgnUserCircularityFull value .miFeatUserSortedByAny.UserData = .mRgnUserCircularityFull 'assign the sort-by measurement! Set mcobjSortedFeatures = .miFeatUserSortedByAny.ValueMcObject 'get sorted indices 'get the median feature index lFeatOfInterest = mcobjSortedFeatures.SelectedValues(mcobjSortedFeatures.VectorLength / 2) Set DisplayedObj = .DisplayedObjects(lFeatOfInterest) DisplayedObj.SetColors &HFFFF00 'Cyan DisplayedObj.SetBorderWidth 2 DisplayedObj.SetLabelText McCStr("Median\r\ncircularity") 'Now wait for the user to look at things before continuing MsgBox "The Convex Hull shows in yellow." + vbCrLf + _ "Largest region shows in magenta." + vbCrLf + _ "Region with median circularity shows in cyan." + vbCrLf + _ "When you press OK, the regions will disappear." 'Clear the regions .Reset 'clear .RegionFeatures myConvexHulls.AutoDisplay = False 'hide convex hulls Set myConvexHulls = Nothing 'release our hold on the mrRgnUserConvexHull 'Note that the .mrRgnUserConvexHull.Value is cached, so the result 'McRegions object will not be fully released until the measurement 'is recomputed. End With 'ActiveImage.RegionFeatures 'Now do a line measurement With .LineFeatures .SetFromMaskMethod = mcsfmmLinesMajorAxisAlignedAvg .Threshold.Execute 'create some lines .CleanUpBordersAndNoise , mcrbNoBorder, 25 'get rid of short lines Output.PrintMessage vbCrLf + "*****" + Str(.Count) + " long line features found." Output.PrintMessage "Bounding Rectangles: " + vbCrLf + McToText( _ .maLnUserBoundingRect, "%.2f") 'Show the median length line in a different color Set mcobjSortedFeatures = .miLnUserSortedByLength.ValueMcObject lFeatOfInterest = mcobjSortedFeatures.SelectedValues(mcobjSortedFeatures.VectorLength / 2) Set DisplayedObj = .DisplayedObjects(lFeatOfInterest) DisplayedObj.SetColors &HFF00FF 'Magenta DisplayedObj.SetBorderWidth 2 DisplayedObj.SetLabelText McSprintf(McCStr("Median\r\nlength = %.0f"), .mLnLength(lFeatOfInterest)) MsgBox "Line with median length shows in magenta." + vbCrLf + _ "When you press OK, the lines will disappear and the image will close." + vbCrLf + _ "A measurement result report is in the Output window." 'Clear the lines .Reset 'clear .LineFeatures End With 'ActiveImage.LineFeatures ActiveWindow.Close 'and finally, close the test image End With 'ActiveImage End Sub 'TestExampleUserMeasurements 'You would generally not want to unregister your measurements, but if you needed to 'for some reason, this sub will do the job. Public Sub UnregisterExampleUserMeasurements() Dim nUnregistered As Long nUnregistered = 0 'Turn off notifies from Engine, so only one registration notify can be fired. 'We do this because repopulating lists of all available measurements can 'be time consuming, and we only want notify sinks to have to do it once 'for all of our un-registrations. Dim EngineHasObject As IMcHasObject Set EngineHasObject = Engine Dim bEntryNotifyState As Boolean bEntryNotifyState = EngineHasObject.FirePropertyChangeNotifies EngineHasObject.FirePropertyChangeNotifies = False If Not GetRegisteredUserMeasurement("mRgnUserCircularityMinCode") Is Nothing Then RegisterUserMeasurement "mRgnUserCircularityMinCode", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("mRgnUserCircularityFull") Is Nothing Then RegisterUserMeasurement "mRgnUserCircularityFull", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("maRgnUserBoundingRect") Is Nothing Then RegisterUserMeasurement "maRgnUserBoundingRect", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("maLnUserBoundingRect") Is Nothing Then RegisterUserMeasurement "maLnUserBoundingRect", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("maRefUserBoundingRect") Is Nothing Then RegisterUserMeasurement "maRefUserBoundingRect", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("mvRgnUserCoordinates") Is Nothing Then RegisterUserMeasurement "mvRgnUserCoordinates", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("mvLnUserCoordinates") Is Nothing Then RegisterUserMeasurement "mvLnUserCoordinates", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("mrRgnUserConvexHull") Is Nothing Then RegisterUserMeasurement "mrRgnUserConvexHull", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("miLnUserSortedByLength") Is Nothing Then RegisterUserMeasurement "miLnUserSortedByLength", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("miRgnUserSortedByArea") Is Nothing Then RegisterUserMeasurement "miRgnUserSortedByArea", Nothing nUnregistered = nUnregistered + 1 End If If Not GetRegisteredUserMeasurement("miFeatUserSortedByAny") Is Nothing Then RegisterUserMeasurement "miFeatUserSortedByAny", Nothing nUnregistered = nUnregistered + 1 End If 'Restore Engine notifies EngineHasObject.FirePropertyChangeNotifies = bEntryNotifyState If nUnregistered <> 0 Then 'were any registered? EventLog.WriteEntry Str(nUnregistered) + " user measurements un-registered.", mceleInformation, 0, mcelcFromUser 'We fire just one "PropertyChanged" notify for all of the un-registrations EngineHasObject.DoFirePropertyChangeNotify ID_IMcEngine_RegisterUserMeasurement End If 'any were registered End Sub 'UnregisterExampleUserMeasurements
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '**** CUserMeasureTemplate.cls **** 'This CLS module is a template for you to modify and fill in to implement your own measurement. 'Replace all occurances of CUserMeasureTemplate with your class module name. 'Some results are shown returning Localized strings and reference "MyLocModuleName". This 'module should be a .LOC file (e.g., CUserMeasureTemplate.loc), copies of which are to be 'placed in Path + "Resources\1033" (for English) and in other Path + "Resources\1NNN" directories 'for other languages. If such a file is missing for the current localization, then no translation is done. 'Look at the existing .LOC files for how to specify translations. Option Explicit Implements McRegionLib.IMcUserMeasure Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant 'This is the only method that is required to do anything. This method must 'return a Variant result that agrees with the Attributes of the measurement 'with respect to type and shape. The default Attributes call for one value 'per feature of a McFeatures ParentOperator to be returned as a 1-D array of Double. 'Below are two examples of such measurements. One uses a For loop to 'compute the result for each feature. The other uses McOMGlobal array operations. On Error GoTo IMcUserMeasure_ComputeValue_Error ' 'Example 1: measure the number of vertices on each feature using a For loop ' Dim myFeat As McFeatures, lF As Long ' Set myFeat = ParentOperator ' If myFeat.Count = 0 Then 'no features? ' IMcUserMeasure_ComputeValue = Empty ' Exit Function ' End If 'no features, so return an Empty result ' 'ELSE we have at least one feature ' ReDim dResultArray(myFeat.Count - 1) As Double ' For lF = 0 To myFeat.Count - 1 ' Dim lVertexCount As Long ' lVertexCount = myFeat.GetFeaturePoints(lF) ' dResultArray(lF) = lVertexCount ' Next lF ' IMcUserMeasure_ComputeValue = dResultArray ' 'Example 2: measure the ratio of polygon perimeter to chain-code perimeter using array operations ' Dim myR As McRegions ' Set myR = ParentOperator ' Set IMcUserMeasure_ComputeValue = _ ' McOpDiv(myR.mRgnPerimeter.ValueMcObject, myR.mRgnPerimeter2.ValueMcObject) ' 'McOpDiv correctly handles zero-length arrays, so no test for myR.Count=0 is needed. On Error GoTo 0 Exit Function IMcUserMeasure_ComputeValue_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_ComputeValue of Class Module CUserMeasureTemplate" End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The display name should be less than 32 characters and it should be localized to 'the current language. IMcUserMeasure_UserDisplayName = _ Localized("Display Name", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The description should be less than 128 characters and it should be localized to 'the current language. IMcUserMeasure_UserDescription = _ Localized("Measurement description of less than 128 characters.", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant On Error GoTo IMcUserMeasure_UserAttributes_Error Select Case AttributeId Case mcmaidDependencies 'Here you can specify the standard dependencies for your measurement and 'arrange to have the UserIsValue stale method called if you have some 'non-standard dependency. In addition, this Attribute case is called only 'once for each measurement supported, so you should do any per McMeasure 'object initialization here. The default dependencies are generally overkill, 'so you can usually avoid unnecessary recomputations by specifying a more 'restricted set of dependencies. Also, if you want the UserIsValue 'method to be called, you must return mcmdfOther among your dependencies. 'Set RangeMin/Max for this measurement, if necessary ' MeasurementObject.RangeMin = 0# 'for example ' MeasurementObject.RangeMax = 1# ' 'Other initialization goes here, if needed (often none is needed). ' IMcUserMeasure_UserAttributes = _ ' mcmdfFeatureData Or mcmdfSpatialCalib 'e.g., if the measure has no luminance dependence Case mcmaidDisconnectMeasure 'This Attribute case is called once for each measurement supported 'at the time that the measurement is disconnected from its McMeasure, 'so you should do any per McMeasure object cleanup here. Be especially 'sure to release any object references that you are holding, so that you 'do not create a "cross-lock" situation that prevents objects from being 'freed. 'You never need to return anything. Case mcmaidAllowedParent 'This will usually be mcmpChildOfPoints, mcmpChildOfLines, mcmpChildOfRegions 'or mcmpChildOfRefFeatures. Only in rare cases will it be anything else. 'The default is mcmpChildOfFeatures, which means that your measurement is 'allowed to be created as a child of any McPoints, McLines or McRegions 'object. This default might be suitable for a few measurements that 'could work on any type of McFeatures (e.g., one that computed the center 'of the bounding rectangle of each feature), but in most cases measurements 'are specific to ' IMcUserMeasure_UserAttributes = _ ' mcmpChildOfRegions 'for example Case mcmaidCategories 'Categories are OR'ed together from the mcMeasurementCategoryFlags 'Only measurements with the mcmcfManualMeasure flag set will be available 'as part of manual measurements; these must be 1-dimensional, one-feature 'per measurement measurements (mcmaidPerFeatureMeasurement returns True 'and mcmaidNofDimensions returns 1). The mcmcfUserDefined flag is 'automatically included, so you don't have to supply it here. 'Note that if your measurement returns angular results, you should set the 'mcmcfAngleResult bit, and if the measurement results ignore calibrations, 'you should set the mcmcfUncalibratedResult bit. These bits affect how 'unit names are generated for the mcmaidUnitsAbbrev and mcmaidUnitsName 'attributes (see below). ' IMcUserMeasure_UserAttributes = _ ' mcmcfFeatureShape Or mcmcfIgnoresHoles Or mcmcfManualMeasure 'for example Case mcmaidPerFeatureMeasurement 'True is the default. Only supply this (as False) if you have some 'non-feature based measurement, or if the measurement is not 'one-result-per-feature. Case mcmaidNofDimensions 'The default value is 1. Only supply this if you have some scalar result '(return 0) or a multidimensional result. Note that measurements with 'variable second dimensions are returned as a 1-D array of Variant, but 'you still need to report 2 dimensions (see mcmaidShape). ' IMcUserMeasure_UserAttributes = _ ' 2 'for example for a 2-dimensional result Case mcmaidShape 'Shape is reported using C/C++ dimension ordering; that is, the 'slowest moving dimension first. VB/VBA dimension ordering is the 'reverse of this. Default is all dimension sizes 0, meaning 'variable, which is suitable for 1-D per-feature measurements and '2-D per-feature measurements where the size of the second, 'fastest moving, dimension varies for each feature (e.g., like 'the mvRgnBranchLengths built-in measurement). For 2-D 'per-feature measurements where the second dimension is of fixed 'size, you supply that here. ' IMcUserMeasure_UserAttributes = _ ' Array( 0, 4) 'for example a measurement returning a RECT for each feature. Case mcmaidResultType 'The default is mcmrtNumeric. Only if your measurement returns a McPoints, 'McLines or McRegions as its value should this be mcmrtMcPoints, 'mcmrtMcLines or mcmrtMcRegions respectively. ' IMcUserMeasure_UserAttributes = _ ' mcmrtMcPoints 'for example a measurement returning McPoints object. Case mcmaidResultValueType 'You not will need to set this unless you are not creating Double numeric 'results. It is recommended that all numeric results be Double, even for 'integral results such as counts; this allows client code to handle all measurements 'symmetrically. The default is vbDouble, which applies only to 'measurements where Attributes(mcmaidResultType) is mcmrtNumeric; for 'all the other result types, the Value property is a McFeatures object and 'this case should be left blank or return vbObject. Case mcmaidResultMcObjectType 'You not will need to set this unless you are not creating Double numeric 'results. It is recommended that all numeric results be Double, even for 'integral results such as counts; this allows client code to handle all measurements 'symmetrically. The default is mcobjTypeREAL, which applies only to 'measurements where Attributes(mcmaidResultType) is mcmrtNumeric; for 'all other the result types, the ValueMcObject property is Nothing and 'this case should be left blank or return mcobjTypeUNKNOWN. Case mcmaidIllustration 'An optional illustration for your measurement. If you supply this it should 'be an 80 pixels wide by 60 high high Picture object. Use the supplied 'examples to start so that your illustration resembles the ones for the 'built-in measurement. ' Dim ProjectPath As String ' ProjectPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(VBE.VBProjects(ThisProject.Name).FileName) ' Dim myPict As New StdPicture ' Set myPict = LoadPicture(ProjectPath + "MyProject\MyIllustration.bmp") ' Set IMcUserMeasure_UserAttributes = myPict Case mcmaidDefaultRangeMin 'The minimum value that your measurement values can be. The default is 'a huge negative number. The value is used to set the intial value of 'the RangeMin property, which is used only for the Filter method' ' IMcUserMeasure_UserAttributes = _ ' 0# 'for example, some measurement that cannot take on negative values Case mcmaidDefaultRangeMax 'The maximum value that your measurement values can be. The default is 'a huge postive number. The value is used to set the intial value of 'the RangeMax property, which is used only for the Filter method' ' IMcUserMeasure_UserAttributes = _ ' 1# 'for example, some measurement that cannot exceed 1.0 Case mcmaidSpatialUnitExponent 'As Long. If no spatial calibration unit applies to this measurement result, 'then this value is zero (which is the default). Otherwise this is the exponent 'to apply to the SpatialCalibration.UnitAbbrev or UnitName (or Pixels if the 'mcmcfUncalibratedResult bit of Attributes(mcmaidCategories) is set). For 'example, Attributes(mcmaidSpatialUnitExponent) mRgnHeterogeneity is 'unitless, so the value is zero; mRgnDensity has no spatial dependence, so 'its value is also zero; for mLnLength the value is 1; for mRgnArea the value 'is 2, and for mRgnIntegratedOD (density/area) the value is -2. The value of 'Attributes(mcmaidIntensityUnitExponent) for these measurments are zero, 1, '0, 0 and 1, respectively. The value for measurements that return angle 'results is zero, but in this case Attributes(mcmaidCategories) needs to have the 'mcmcfAngleResult bit set. ' IMcUserMeasure_UserAttributes = _ ' 2& 'for example, some measurement needing units of area Case mcmaidIntensityUnitExponent 'As Long. If no intensity calibration unit applies to this measurement result, 'then this value is zero (which is the default). Otherwise this is the exponent 'to apply to the IntensityCalibration.UnitAbbrev or UnitName (or raw pixel 'luminance values if the mcmcfUncalibratedResult bit of Attributes(mcmaidCategories) is set). 'For example, Attributes(mcmaidIntensityUnitExponent) mRgnHeterogeneity is 'unit-less, so the value is 0; mLnLength has no intensity dependence, so its 'value is zero; for mRgnDensity the value is 1, and for mRgnIntegratedOD '(density/area) the value is 1. The value of Attributes(mcmaidSpatialUnitExponent) 'for these measurements are zero, 1, 0, -2, respectively. ' IMcUserMeasure_UserAttributes = _ ' 1& 'for example, some measurement needing units of density Case mcmaidUnitsAbbrev 'You will generally not need to return this attribute if you have properly handled the 'mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent attributes, above, because 'they are used to compute the proper units by default. However, in rare circumstances 'you may wish to supply your own units; you will have to do so if your result is an 'angular measure that returns radians instead of degrees. ' IMcUserMeasure_UserAttributes = _ ' Localized("rad", "MyLocModuleName") 'e.g., for an angle measure returning radians rather than degrees Case mcmaidUnitsName 'You will generally not need to return this attribute if you have properly handled the 'mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent attributes, above, because 'they are used to compute the proper units by default. However, in rare circumstances 'you may wish to supply your own units; you will have to do so if your result is an 'angular measure that returns radians instead of degrees. ' IMcUserMeasure_UserAttributes = _ ' Localized("radians", "MyLocModuleName") 'e.g., for an angle measure returning radians rather than degrees Case mcmaidAbbreviation 'You will almost always want to return this attribute to give your measurement a short 'abbreviation, suitable for labeling measurements on the image. Try to keep it to 5 'characters or less. ' IMcUserMeasure_UserAttributes = _ ' Localized("Nvrts", "MyLocModuleName") 'e.g., for a "Number of vertices" measurement Case Else End Select 'AttributeId On Error GoTo 0 Exit Function IMcUserMeasure_UserAttributes_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_UserAttributes of Class Module CUserMeasureTemplate" End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean On Error GoTo IMcUserMeasure_UserIsValueStale_Error 'This method will only be called if the IMcUserMeasure_UserAttributes mcmaidDependencies 'case returns mcmdfOther among the mcMeasurementDependencyFlags. In that case, then 'this method will be called before any Value access to see if the value needs to 'be recomputed. The method must return True if the measurement is stale for some 'reason beyond the standard mcMeasurementDependencyFlags returned '(which are already taken care of automatically, and so do not need to be tested for 'here). ' 'Example: see if the maRgnRadii measurement is stale or has a ValueSequence greater than ' 'ours. The maRgnRadii measurement can go stale if its NumAngles property is assigned-to, ' 'and no standard dependency can detect this automatically. ' ' ' Dim myParentRegions As McRegions ' Set myParentRegions = MeasurementObject.Attributes(mcmaidParent) ' IMcUserMeasure_UserIsValueStale = _ ' (myParentRegions.maRgnRadii.ValueSequence(MeasurementObject.ValueSequence) = 0) On Error GoTo 0 Exit Function IMcUserMeasure_UserIsValueStale_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_UserIsValueStale of Class Module CUserMeasureTemplate" End Function
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '***** CUserMeasureCircularityMinCode.cls ****** 'This class module shows the absolute minimum code necessary to implement a measurement 'with a result that has one scalar value per feature in the parent McFeatures object. 'In this case you only need to return a 1-dimensional array of values from the 'ComputeValue method. See the CUserMeasureCircularityFull.cls module for a more 'complete implementation of this measurement. ' ' Note: Circularity as computed here is just the inverse of the built-in mRgnRoundness measurement. Option Explicit Implements McRegionLib.IMcUserMeasure Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant Dim myRegions As McRegions Set myRegions = ParentOperator Dim mcobjVal As McObject Set mcobjVal = _ McOpMul(McOpDiv(myRegions.mRgnPolygonialArea, McPow(myRegions.mRgnPerimeter, 2)), 4# * 3.1416) 'Set results where perimeter or area is zero to missing mcobjVal.SelectedValues(McOpOR(McOpEQ(myRegions.mRgnPerimeter, 0#), McOpEQ(myRegions.mRgnPolygonialArea, 0#))) = McMissingDouble Set IMcUserMeasure_ComputeValue = mcobjVal 'return the McObject result 'IMcUserMeasure_ComputeValue = mcobjVal 'this also works, but is less efficient End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean End Function
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '***** CUserMeasureCircularityFull.cls ****** 'This class module shows the code necessary to implement a measurement that is 'indistinguishable from a built-in measurement. This example implements a 'measurement that has one scalar value per feature in the parent McFeatures 'object, which is typical of most measurements. ' ' See McUserMeasure_Examples.bas for how to register this measurement. ' ' Note: Circularity as computed here is just the inverse of the built-in mRgnRoundness measurement. Option Explicit Implements McRegionLib.IMcUserMeasure Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant On Error GoTo IMcUserMeasure_ComputeValue_Error Dim myRegions As McRegions Set myRegions = ParentOperator Dim mcobjVal As McObject Set mcobjVal = _ McOpMul(McOpDiv(myRegions.mRgnPolygonialArea, McPow(myRegions.mRgnPerimeter, 2)), 4# * 3.1416) 'Set results where perimeter or area is zero to missing mcobjVal.SelectedValues(McOpOR(McOpEQ(myRegions.mRgnPerimeter, 0#), McOpEQ(myRegions.mRgnPolygonialArea, 0#))) = McMissingDouble Set IMcUserMeasure_ComputeValue = mcobjVal 'return the McObject result 'IMcUserMeasure_ComputeValue = mcobjVal 'this also works, but is less efficient On Error GoTo 0 Exit Function IMcUserMeasure_ComputeValue_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_ComputeValue of Class Module CUserMeasureCircularityFull" End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The display name should be less than 32 characters and it should be localized to 'the current language. IMcUserMeasure_UserDisplayName = _ Localized("Circularity", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The description should be less than 128 characters and it should be localized to 'the current language. IMcUserMeasure_UserDescription = _ Localized("Circularity is (Area / Perimeter^2) * 4 * PI.", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant Select Case AttributeId Case mcmaidDependencies 'Here is where we do any required intialization, but none is needed for this measurement. 'We do NOT return mcmdfOther, because the below cover all our dependencies IMcUserMeasure_UserAttributes = _ mcmdfFeatureData Or mcmdfSpatialCalib ' Case mcmaidDisconnectMeasure ' 'No cleanup needed for this measurement. Case mcmaidAllowedParent IMcUserMeasure_UserAttributes = _ mcmpChildOfRegions Case mcmaidCategories IMcUserMeasure_UserAttributes = _ mcmcfFeatureShape Or mcmcfIgnoresHoles Or mcmcfManualMeasure ' Case mcmaidPerFeatureMeasurement ' 'True is the default, which works for us. ' Case mcmaidNofDimensions ' 'The default value is 1, which is OK by us. ' Case mcmaidShape ' 'Default is all dimension sizes 0, which is OK by us ' Case mcmaidResultType ' 'The default is mcmrtNumeric which is OK by us. ' Case mcmaidResultValueType ' The default is vbDouble, which is OK by us. ' Case mcmaidResultMcObjectType ' 'The default is mcobjTypeREAL, which is OK by us. Case mcmaidIllustration 'An optional 80 by 60 illustration for the measurement. Dim ProjectPath As String ProjectPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(VBE.VBProjects(ThisProject.Name).FileName) Dim myPict As New StdPicture Set myPict = LoadPicture(ProjectPath + "\Circularity.bmp") Set IMcUserMeasure_UserAttributes = myPict Case mcmaidDefaultRangeMin IMcUserMeasure_UserAttributes = _ 0# 'Circularity ranges from 0 to 1 Case mcmaidDefaultRangeMax IMcUserMeasure_UserAttributes = _ 1# 'Circularity ranges from 0 to 1 ' Case mcmaidSpatialUnitExponent ' 'The default is zero, which is OK since this result in unit-less. ' Case mcmaidIntensityUnitExponent ' 'The default is zero, which is OK since this result in unit-less. ' Case mcmaidUnitsAbbrev ' Case mcmaidUnitsName ' 'The default handles this properly based on mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent. Case mcmaidAbbreviation IMcUserMeasure_UserAttributes = "Circ" 'measurement abbreviated name Case Else End Select 'AttributeId End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean 'Nothing needs to be done here, as the automatically detected dependencies 'are suffiecient (see the mcmaidDependencies Case, above). End Function
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '***** CUserMeasureBoundingRect.cls ****** 'Note: this a more advanced and unusual example than that shown in 'CUserMeasureCircularityFull, so you should be familiar with that 'one first. 'This example implements a measurement that has one fixed length array value per 'feature in the ancestor McFeatures object. The measurement reports the 'bounding rect of each feature as a length-4 array of 'Double in the order Left, Top, Right, Bottom. The results are 'in calibrated coordinates. ' 'See McUserMeasure_Examples.bas for how to register this measurement. The 'measurement will work with any ancestor McFeatures but it is registered 'under three names to work as an immediate child of a McLines, McRegions or 'a McRefFeatures with names maLnUserBoundingRect, maRgnUserBoundingRect 'and maRefUserBoundingRect, respectively. ' Option Explicit Implements McRegionLib.IMcUserMeasure Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant On Error GoTo IMcUserMeasure_ComputeValue_Error 'Measurement works with any ancestor McFeatures Dim myFeat As McFeatures, lF As Long Set myFeat = AncestorOfOperator(MeasurementObject, "McFeatures") If myFeat.Count = 0 Then 'no features? IMcUserMeasure_ComputeValue = Empty Exit Function End If 'no features, so return an Empty result 'ELSE we have at least one feature ReDim d2DResultArray(3, myFeat.Count - 1) As Double For lF = 0 To myFeat.Count - 1 Dim rectB As SINGLERECT rectB = myFeat.BoundingRect(lF) 'calibrate the results, if necessary If Not myFeat.SpatialCalibration Is Nothing Then myFeat.SpatialCalibration.CalibrateFloatPoints rectB End If d2DResultArray(0, lF) = rectB.Left d2DResultArray(1, lF) = rectB.Top d2DResultArray(2, lF) = rectB.Right d2DResultArray(3, lF) = rectB.Bottom Next lF IMcUserMeasure_ComputeValue = d2DResultArray On Error GoTo 0 Exit Function IMcUserMeasure_ComputeValue_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_ComputeValue of Class Module CUserMeasureBoundingRect" End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The display name should be less than 32 characters and it should be localized to 'the current language. IMcUserMeasure_UserDisplayName = _ Localized("Bounding Rectangle", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The description should be less than 128 characters and it should be localized to 'the current language. IMcUserMeasure_UserDescription = _ Localized("Bounding rectangle as an array in the order Left, Top, Right, Bottom", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant Select Case AttributeId Case mcmaidDependencies 'Here is where we do any required intialization, but none is needed for this measurement. 'We do NOT return mcmdfOther, because the below cover all our dependencies IMcUserMeasure_UserAttributes = _ mcmdfFeatureData Or mcmdfSpatialCalib ' Case mcmaidDisconnectMeasure ' 'No cleanup needed for this measurement. Case mcmaidAllowedParent IMcUserMeasure_UserAttributes = _ mcmpDescendentOfFeatures 'works with any ancestor McFeatures Case mcmaidCategories IMcUserMeasure_UserAttributes = _ mcmcfFeatureShape Or mcmcfIgnoresHoles Or mcmcfSize Or mcmcfPosition ' Case mcmaidPerFeatureMeasurement ' 'True is the default, which works for us. Case mcmaidNofDimensions IMcUserMeasure_UserAttributes = 2 Case mcmaidShape 'Shape is reported using C/C++ dimension ordering; that is, 'the slowest moving dimension first (which in this case 'is the variable each-feature dimensions and so is reported 'as zero). Note that VB/VBA dimension ordering is the 'reverse of this (e.g., see the ComputeValue method above). IMcUserMeasure_UserAttributes = Array(0, 4) ' Case mcmaidResultType ' 'The default is mcmrtNumeric which is OK by us. ' Case mcmaidResultValueType ' The default is vbDouble, which is OK by us. ' Case mcmaidResultMcObjectType ' 'The default is mcobjTypeREAL, which is OK by us. Case mcmaidIllustration 'An optional 80 by 60 illustration for the measurement. Dim ProjectPath As String ProjectPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(VBE.VBProjects(ThisProject.Name).FileName) Dim myPict As New StdPicture Set myPict = LoadPicture(ProjectPath + "\BoundingRect.bmp") Set IMcUserMeasure_UserAttributes = myPict ' Case mcmaidDefaultRangeMin ' Case mcmaidDefaultRangeMin ' 'Attributes(mcmaidCanFilter) is False for this measurement (because it is not a ' 'one-value-per-feature measurement), so the RangeMin and RangeMax have no use ' 'and can just be left alone. Case mcmaidSpatialUnitExponent IMcUserMeasure_UserAttributes = 1& 'values are coordinates ' Case mcmaidIntensityUnitExponent ' 'The default is zero, which is OK since this result has no luminance unit ' Case mcmaidUnitsAbbrev ' Case mcmaidUnitsName ' 'The default handles this properly based on mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent. Case mcmaidAbbreviation IMcUserMeasure_UserAttributes = "Brect" 'measurement abbreviated name Case Else End Select 'AttributeId End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean 'Nothing needs to be done here, as the automatically detected dependencies 'are suffiecient (see the mcmaidDependencies Case, above). End Function
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '***** CUserMeasureCoordinates.cls ****** 'Note: this a more advanced and unusual example than that shown in 'CUserMeasureCircularityFull or CUserMeasureBoundingRect, so you should be familiar 'with those first. 'This example implements a measurement that results in a variable number of values 'per feature in the ancestor McFeatures object. The measurement reports the 'coordinate points each feature as a length-N array of 2 Single (!) X and Y values. 'The results are in calibrated coordinates. This measurement is unusual in that 'it returns measurements as Single rather than Double type (see the 'mcmaidResultValueType and mcmaidResultMcObjectType cases in the UserAttributes method). ' 'See McUserMeasure_Examples.bas for how to register this measurement. The 'measurement will work with any ancestor McFeatures but it is registered 'under two names to work as an immediate child of a McLines or McRegions 'with names mvLnUserCoordinates, mvRgnUserCoordinates, respectively. ' Option Explicit Implements McRegionLib.IMcUserMeasure Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant On Error GoTo IMcUserMeasure_ComputeValue_Error 'Measurement works with any ancestor McFeatures Dim myFeat As McFeatures, lF As Long Set myFeat = AncestorOfOperator(MeasurementObject, "McFeatures") If myFeat.Count = 0 Then 'no features? IMcUserMeasure_ComputeValue = Empty Exit Function End If 'no features, so return an Empty result 'ELSE we have at least one feature ReDim f1DVariantArray(myFeat.Count - 1) As Variant For lF = 0 To myFeat.Count - 1 'Find number of coordinates for this feature Dim lNcoords As Long lNcoords = myFeat.GetFeaturePoints(lF) If lNcoords = 0 Then GoTo NextLoop 'if no coordinates, leave this feature's result Empty 'Note: results are Single rather than the usual Double type ReDim farrayFeatCoords(1, lNcoords - 1) As Single lNcoords = myFeat.GetFeaturePoints(lF, farrayFeatCoords) 'calibrate the results, if necessary If Not myFeat.SpatialCalibration Is Nothing Then myFeat.SpatialCalibration.CalibrateFloatPoints farrayFeatCoords End If f1DVariantArray(lF) = farrayFeatCoords NextLoop: Next lF IMcUserMeasure_ComputeValue = f1DVariantArray On Error GoTo 0 Exit Function IMcUserMeasure_ComputeValue_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_ComputeValue of Class Module CUserMeasureCoordinates" End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The display name should be less than 32 characters and it should be localized to 'the current language. IMcUserMeasure_UserDisplayName = _ Localized("Coordinates", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The description should be less than 128 characters and it should be localized to 'the current language. IMcUserMeasure_UserDescription = _ Localized("Feature coordinates as an array of 2 values, X and Y.", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant Select Case AttributeId Case mcmaidDependencies 'Here is where we do any required intialization, but none is needed for this measurement. 'We do NOT return mcmdfOther, because the below cover all our dependencies IMcUserMeasure_UserAttributes = _ mcmdfFeatureData Or mcmdfSpatialCalib ' Case mcmaidDisconnectMeasure ' 'No cleanup needed for this measurement. Case mcmaidAllowedParent IMcUserMeasure_UserAttributes = _ mcmpDescendentOfFeatures 'works with any ancestor McFeatures Case mcmaidCategories IMcUserMeasure_UserAttributes = _ mcmcfFeatureShape Or mcmcfIgnoresHoles Or mcmcfSize Or mcmcfPosition ' Case mcmaidPerFeatureMeasurement ' 'True is the default, which works for us. Case mcmaidNofDimensions 'Even though the result Value is a 1-D array of Variant, we report 'our result as having 3 dimensions. This is because each Variant 'in the result Value array has 2 dimensions (N X,Y coordinate pairs). IMcUserMeasure_UserAttributes = 3 Case mcmaidShape 'Shape is reported using C/C++ dimension ordering; that is, 'the slowest moving dimension first (which in this case 'is the variable each-feature dimensions and so is reported 'as zero). Note that VB/VBA dimension ordering is the 'reverse of this (e.g., see the ComputeValue method above). 'In this case our measurement is a 1-D array of Variant, each 'holding a 2-D array of values. Below is how we report this 'shape; the second 0 signals that the result is an array of Variant. IMcUserMeasure_UserAttributes = Array(0, 0, 2) ' Case mcmaidResultType ' 'The default is mcmrtNumeric which is OK by us. Case mcmaidResultValueType 'This measurement in unusual in that it returns Single rather than Double type IMcUserMeasure_UserAttributes = vbSingle Case mcmaidResultMcObjectType 'This measurement in unusual in that it returns Single rather than Double type IMcUserMeasure_UserAttributes = mcobjTypeFLOAT Case mcmaidIllustration 'An optional 80 by 60 illustration for the measurement. Dim ProjectPath As String ProjectPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(VBE.VBProjects(ThisProject.Name).FileName) Dim myPict As New StdPicture Set myPict = LoadPicture(ProjectPath + "\Coordinates.bmp") Set IMcUserMeasure_UserAttributes = myPict ' Case mcmaidDefaultRangeMin ' Case mcmaidDefaultRangeMin ' 'Attributes(mcmaidCanFilter) is False for this measurement (because it is not a ' 'one-value-per-feature measurement), so the RangeMin and RangeMax have no use ' 'and can just be left alone. Case mcmaidSpatialUnitExponent IMcUserMeasure_UserAttributes = 1& 'values are coordinates ' Case mcmaidIntensityUnitExponent ' 'The default is zero, which is OK since this result has no luminance unit ' Case mcmaidUnitsAbbrev ' Case mcmaidUnitsName ' 'The default handles this properly based on mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent. Case mcmaidAbbreviation IMcUserMeasure_UserAttributes = "XY" 'measurement abbreviated name Case Else End Select 'AttributeId End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean 'Nothing needs to be done here, as the automatically detected dependencies 'are suffiecient (see the mcmaidDependencies Case, above). End Function
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '***** CUserMeasureConvexHull.cls ****** 'Note: this a more advanced and unusual example than that shown in 'CUserMeasureCircularityFull, so you should be familiar with that 'one first. 'This example implements a measurement that returns a McRegions result. 'In this case, the result McRegions features are the convex hulls 'of the ancestor McRegions features. The result McRegions is created 'as a child of the measured ancestor McRegions. ' 'See McUserMeasure_Examples.bas for how to register this measurement. The 'measurement is registered as mrRgnUserConvexHull to work as an immediate 'child of a McRegions object. ' 'Note that this measurement is the same as the built-in mrRgnConvexHull measurement. ' Option Explicit Implements McRegionLib.IMcUserMeasure Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant On Error GoTo IMcUserMeasure_ComputeValue_Error 'Works with nearest ancestor McRegions, not just the immediate parent Dim myAncestorRegions As McRegions, lF As Long Set myAncestorRegions = AncestorOfOperator(MeasurementObject, "McRegions") 'Create empty result McRegions as a child of myAncestorRegions Dim myResultRegions As McRegions Set myResultRegions = CreateOperator("McRegions", myAncestorRegions) For lF = 0 To myAncestorRegions.Count - 1 Dim varConvexHull As Variant varConvexHull = Empty 'start empty myAncestorRegions.GetFeaturePointsEx lF, varConvexHull, mcotConvex myResultRegions.SetFeaturePoints lF, varConvexHull Next lF Set IMcUserMeasure_ComputeValue = myResultRegions On Error GoTo 0 Exit Function IMcUserMeasure_ComputeValue_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_ComputeValue of Class Module CUserMeasureConvexHull" End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The display name should be less than 32 characters and it should be localized to 'the current language. IMcUserMeasure_UserDisplayName = _ Localized("Convex Hull as Regions", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The description should be less than 128 characters and it should be localized to 'the current language. IMcUserMeasure_UserDescription = _ Localized("Convex hulls of region boundaries, exposed as a McRegions object.", "MyLocModuleName") End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant Select Case AttributeId Case mcmaidDependencies 'Here is where we do any required intialization, but none is needed for this measurement. 'We do NOT return mcmdfOther, because the below cover all our dependencies IMcUserMeasure_UserAttributes = _ mcmdfFeatureData ' Or mcmdfSpatialCalib 'no need for calibration dependency ' Case mcmaidDisconnectMeasure ' 'No cleanup needed for this measurement. Case mcmaidAllowedParent IMcUserMeasure_UserAttributes = _ mcmpDescendentOfFeatures 'works with any ancestor McRegions Case mcmaidCategories IMcUserMeasure_UserAttributes = _ mcmcfFeatureShape Or mcmcfIgnoresHoles Or mcmcfSize Or mcmcfPosition ' Case mcmaidPerFeatureMeasurement ' 'True is the default, which works for us. ' Case mcmaidNofDimensions ' 'This Attribute applies only to numeric measurements ' Case mcmaidShape ' 'This Attribute applies only to numeric measurements Case mcmaidResultType 'This is the key attribute that tells the caller that the result 'will be a McRegions object instead of some mcmrtNumeric result. IMcUserMeasure_UserAttributes = mcmrtMcRegions ' Case mcmaidResultValueType ' 'This Attribute applies only to numeric measurements ' Case mcmaidResultMcObjectType ' 'This Attribute applies only to numeric measurements Case mcmaidIllustration 'An optional 80 by 60 illustration for the measurement. Dim ProjectPath As String ProjectPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(VBE.VBProjects(ThisProject.Name).FileName) Dim myPict As New StdPicture Set myPict = LoadPicture(ProjectPath + "\ConvexHull.bmp") Set IMcUserMeasure_UserAttributes = myPict ' Case mcmaidDefaultRangeMin ' Case mcmaidDefaultRangeMin ' 'Attributes(mcmaidCanFilter) is False for this measurement (because it is not a ' 'numeric measurement), so the RangeMin and RangeMax have no use ' 'and can just be left alone. ' Case mcmaidSpatialUnitExponent ' Case mcmaidIntensityUnitExponent ' 'The default is zero, which is OK since this result is not a numeric measurement ' Case mcmaidUnitsAbbrev ' Case mcmaidUnitsName ' 'The default handles this properly based on mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent. Case mcmaidAbbreviation IMcUserMeasure_UserAttributes = "CvHull" 'measurement abbreviated name Case Else End Select 'AttributeId End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean 'Nothing needs to be done here, as the automatically detected dependencies 'are suffiecient (see the mcmaidDependencies Case, above). End Function
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END '***** CUserMeasureSortedIndices.cls ****** 'Note: this a more advanced and unusual example than that shown in 'CUserMeasureCircularityFull or any of the other example classes, 'so you should be familiar with all of the others first. 'This example illustrates several unusual measurement possiblitilits: ' ' 1. The measurement results are based on an ancestor McFeatures, but 'the results are not one result associated with each feature the way that 'most measurements are. Thus, for this measurement 'Attributes(mcmaidPerFeatureMeasurement) is False. ' ' 2. The measurement uses per-McMeasure data to do its work. Remember 'that only one McUserMeasure instance is registered under a given name, 'so this one instance has to service all McMeasure instances that are 'created on legal parent objects (and there may be many such). For 'this measurement, per-McMeasure data is stored in the McMeasure.UserData 'property designed for this purpose. ' ' 3. The standard, automatically serviced dependencies are not 'adequate for this measurement, so it implements the UserIsValueStale 'method to determine when the measurement value needs to be recomputed. ' ' 4. The measurement results are returned as Long integral values rather than 'as floating point values. 'The measurment result is a 1-dimensional array of feature indices 'showing the sorted order of some per-feature measurement. 'The length of the array is equal to the McFeatures.Count of the ancestor 'McFeatures upon which the measurement is being made, but instead of 'each value corresponding to a feature, it is instead an index of 'the feature that holds that sort order for the measurement being sorted on. ' 'A per feature measurement showing the rank order of each feature might 'also be useful (that is where the measurement result associated with 'the zero'th feature is its rank among all features for the sorted 'measurement). You might name such a class something like '"CUserMeasureValueRank". We leave it to you as an exercise; comments 'in the ComputeValue method show you one way to do the ranking. 'The class determines which measurement it is to sort by in one of two 'ways. It can analyse the name of the connected McMeasure object and deduce the 'desired desired measurement to use for sorting. Or it can use a McMeasure 'instance assigned to McMeasure.UserData as the sort measurement. See the 'UserAttributes mcmaidDependencies case for the automatic detection code. If the 'measurement name starts with the substring "miPt...SortedBy", "miLn...SortedBy", '"miRgn...SortedBy" or "miRef...SortedBy" then anything after that is appended to 'either "mPt", "mLn", "mRgn" or "mRef" respectively. For example the following 'measurement names: "miLnUserSortedByLength", "miRefUserSortedByDistance" or '"miRgnUserSortedByArea" will automatically sort on mLnLength, mRefDistance or 'mRgnArea, respectively. By contrast, a measurement named "miFeatUserSortedByAny" 'would need to have a statement such as the following: ' With ActiveImage.PointFeatures ' Set .miFeatUserSortedByAny.UserData = .mPtDensity ' End With 'in order for miFeatUserSortedByAny to sort on the mPtDensity measurement. ' 'The DisplayName, Description, certain Attributes and of course the 'computed value all depend on the sort-by measurement held in the 'McMeasure.UserData property. ' 'See McUserMeasure_Examples.bas for how to register this measurement. The 'measurement will work with any ancestor McFeatures but it is registered 'under three names to work as an immediate child of a McLines or McRegions, or as 'a descendent of any McFeatures with names miLnUserSortedByLength, miRgnUserSortedByArea 'and miFeatUserSortedByAny, respectively. Examples of using these measurements are 'shown in the TestExampleUserMeasurements sub. ' Option Explicit Implements McRegionLib.IMcUserMeasure 'Note: utility function's fGetOurSortedByMeasure and fAnalyseOurName are at the end of this module Private Function IMcUserMeasure_ComputeValue(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal ParentOperator As Object) As Variant On Error GoTo IMcUserMeasure_ComputeValue_Error Dim OurSortedByMeasure As McRegionLib.McMeasure Set OurSortedByMeasure = fGetOurSortedByMeasure(MeasurementObject) Dim mcobjSortOrder As McObject Set mcobjSortOrder = McSort(OurSortedByMeasure.ValueMcObject) Set IMcUserMeasure_ComputeValue = mcobjSortOrder 'that's all folks! ' '******** ' 'To implement the "CUserMeasureValueRank" class suggested in the introductory comments, ' 'all we would need to do is the following, starting from the above: ' ' ' Dim nFeatures As Long ' nFeatures = mcobjSortOrder.VectorLength ' 'Make a ramp of feature indicies, 0 to nFeatures-1 ' Dim mcobjFeatureRanks As McObject ' Set mcobjFeatureRanks = McOpCast(McOpFillIn(0&, nFeatures), "REAL") ' 'For the "CUserMeasureValueRank", we keep the mcmaidResultValueType the standard vbDouble ' 'and the mcmaidResultMcObjectType the standard mcobjTypeREAL. McOpCast, above, casts ' 'the LONG type result from the McOpFillIn to mcobjTypeREAL. ' ' 'And the ranked result is just the ramp of feature indices selected in sorted order ' IMcUserMeasure_ComputeValue = mcobjFeatureRanks.SelectedValues(mcobjSortOrder) ' 'Note that we do not use a Set assignment for the return value above. This is ' 'because the SelectedValues property returns a numeric Variant, not a McObject instance. On Error GoTo 0 Exit Function IMcUserMeasure_ComputeValue_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_ComputeValue of Class Module CUserMeasureSortedIndices" End Function Private Function IMcUserMeasure_UserDisplayName(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The display name should be less than 32 characters and it should be localized to 'the current language. On Error GoTo IMcUserMeasure_UserDisplayName_Error Dim OurSortedByMeasure As McRegionLib.McMeasure Set OurSortedByMeasure = fGetOurSortedByMeasure(MeasurementObject, False) If OurSortedByMeasure Is Nothing Then 'don't know what we will be sorting by? IMcUserMeasure_UserDisplayName = _ Localized("Sorted Indices", "MyLocModuleName") 'then use generic name Else 'we have a sort-by measurement Dim strBase As String strBase = Localized("Sorted by ", "MyLocModuleName") IMcUserMeasure_UserDisplayName = _ strBase + OurSortedByMeasure.DisplayName 'something like "Sorted by Pixel Area" End If 'we have a sort-by measurement On Error GoTo 0 Exit Function IMcUserMeasure_UserDisplayName_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_UserDisplayName of Class Module CUserMeasureSortedIndices" End Function Private Function IMcUserMeasure_UserDescription(ByVal MeasurementObject As McRegionLib.IMcMeasure) As String 'The description should be less than 128 characters and it should be localized to 'the current language. On Error GoTo IMcUserMeasure_UserDescription_Error Dim OurSortedByMeasure As McRegionLib.McMeasure Set OurSortedByMeasure = fGetOurSortedByMeasure(MeasurementObject, False) Dim strBase As String strBase = Localized("Feature indices sorted by ", "MyLocModuleName") If OurSortedByMeasure Is Nothing Then 'don't know what we will be sorting by? IMcUserMeasure_UserDescription = _ strBase + Localized(" another measurement", "MyLocModuleName") 'then use generic description Else 'we have a sort-by measurement IMcUserMeasure_UserDescription = _ strBase + OurSortedByMeasure.DisplayName 'something like "... sorted by Pixel Area" End If 'we have a sort-by measurement On Error GoTo 0 Exit Function IMcUserMeasure_UserDescription_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_UserDescription of Class Module CUserMeasureSortedIndices" End Function Private Function IMcUserMeasure_UserAttributes(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByVal AttributeId As McRegionLib.mcMeasurementAttributeID) As Variant On Error GoTo IMcUserMeasure_UserAttributes_Error Select Case AttributeId Case mcmaidDependencies 'Here we intialize for the measurement to sort on, if we can get it from our name Dim strSortMeasure As String, OurParentType As mcMeasurementParent OurParentType = fAnalyseOurName(MeasurementObject, strSortMeasure) If Len(strSortMeasure) <> 0 Then 'got our sort measurement from our name? Dim siblingMeasures As McMeasures, sortMeasure As McMeasure Set siblingMeasures = ParentOfOperator(MeasurementObject).Measures Set sortMeasure = siblingMeasures(strSortMeasure) 'look it up by name If Not sortMeasure Is Nothing Then MeasurementObject.UserData = sortMeasure 'set our UserData to our measurement 'And we know that our dependencies are the same as our sort measurement's IMcUserMeasure_UserAttributes = _ sortMeasure.Attributes(mcmaidDependencies) Exit Function End If 'sortMeasure Is Not Nothing 'ELSE sortMeasure Is Nothing 'Tell the caller that we could not get their measurement MsgBox MeasurementObject.Attributes(mcmaidName) + _ " resolved to a sort-by measurement name (" + strSortMeasure + _ ") but no measurement by that name could be found." End If 'we could get our sort measurement from our name 'Else just leave MeasurementObject.UserData Empty 'We don't know our sort-by measurement as yet, so we return 'mcmdfOther, because we will need to determine our stale state in UserIsValueStale IMcUserMeasure_UserAttributes = mcmdfOther Case mcmaidDisconnectMeasure 'For cleanup, we just empty our UserData, thereby freeing our sort-by McMeasure object MeasurementObject.UserData = Empty Case mcmaidCanBeEnabled 'We can only be enabled if we have a sort-by measurement IMcUserMeasure_UserAttributes = _ Not fGetOurSortedByMeasure(MeasurementObject, False) Is Nothing Case mcmaidAllowedParent 'Just return the default of mcmpDescendentOfFeatures IMcUserMeasure_UserAttributes = mcmpDescendentOfFeatures Case mcmaidCategories IMcUserMeasure_UserAttributes = _ mcmcfFeatureShape Or mcmcfIgnoresHoles Or mcmcfSize Or mcmcfPosition Case mcmaidPerFeatureMeasurement 'Since the measurement value at index N is not associated with feature index N, 'this measurement is NOT a per-feature measurement. Were you to 'implement the "CUserMeasureValueRank" class suggested in the introductory 'remarks, then this would be a per-feature measurement, so you would return True. IMcUserMeasure_UserAttributes = False Case mcmaidNofDimensions IMcUserMeasure_UserAttributes = 1 'results are a 1-D array of feature indices Case mcmaidShape 'The result is a 1-dimensional array where the number of values in the array 'depends on the number of values in the sort-by measurement Value IMcUserMeasure_UserAttributes = 0 ' Case mcmaidResultType ' 'The default is mcmrtNumeric which is OK by us. Case mcmaidResultValueType 'We compute our Value as an array of Long rather than the usual Double type IMcUserMeasure_UserAttributes = vbLong Case mcmaidResultMcObjectType 'We compute our ValueMcObject as mcobjTypeINTEGER rather than the usual mcobjTypeREAL type IMcUserMeasure_UserAttributes = mcobjTypeINTEGER Case mcmaidIllustration 'An optional 80 by 60 illustration for the measurement. Dim ProjectPath As String ProjectPath = CreateObject("Scripting.FileSystemObject").GetParentFolderName(VBE.VBProjects(ThisProject.Name).FileName) Dim myPict As New StdPicture Set myPict = LoadPicture(ProjectPath + "\SortedIndices.bmp") Set IMcUserMeasure_UserAttributes = myPict ' Case mcmaidDefaultRangeMin ' Case mcmaidDefaultRangeMin ' 'Attributes(mcmaidCanFilter) is False for this measurement (because it is not a ' 'one-value-per-feature measurement), so the RangeMin and RangeMax have no use ' 'and can just be left alone. ' Case mcmaidSpatialUnitExponent ' Case mcmaidIntensityUnitExponent ' 'The default is zero, which is OK since this result is not a numeric measurement ' Case mcmaidUnitsAbbrev ' Case mcmaidUnitsName ' 'The default handles this properly based on mcmaidSpatialUnitExponent and mcmaidIntensityUnitExponent. Case mcmaidAbbreviation Dim OurSortedByMeasure As McRegionLib.McMeasure Set OurSortedByMeasure = fGetOurSortedByMeasure(MeasurementObject, False) If OurSortedByMeasure Is Nothing Then 'don't know what we will be sorting by? IMcUserMeasure_UserAttributes = Localized("SrtdI", "MyLocModuleName") 'then use generic name Else 'we have a sort-by measurement Dim strBase As String strBase = Localized("Srtd", "MyLocModuleName") IMcUserMeasure_UserAttributes = strBase + _ OurSortedByMeasure.Attributes(mcmaidAbbreviation) 'something like "SrtdA" End If 'we have a sort-by measurement Case Else End Select 'AttributeId On Error GoTo 0 Exit Function IMcUserMeasure_UserAttributes_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_UserAttributes of Class Module CUserMeasureSortedIndices" End Function Private Function IMcUserMeasure_UserIsValueStale(ByVal MeasurementObject As McRegionLib.IMcMeasure) As Boolean On Error GoTo IMcUserMeasure_UserIsValueStale_Error 'We may or may not get called here, depending on what we had to do in the mcmaidDependencies 'case of the UserAttributes method above. However, if we are called, then we need to figure 'out if our measurement value is older than the current measurement value of our 'sort-by measurement, or if our sort-by measurement is currently stale. The single 'statement below does this whole job by comparing the ValueSequence of our connected 'MeasurementObject with the ValueSequence of the sort-by measurement upon which we depend. 'Note that the ValueSequence of the sort-by measurement will be zero if that measurement 'is stale. IMcUserMeasure_UserIsValueStale = _ (fGetOurSortedByMeasure(MeasurementObject).ValueSequence(MeasurementObject.ValueSequence) = 0) On Error GoTo 0 Exit Function IMcUserMeasure_UserIsValueStale_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IMcUserMeasure_UserIsValueStale of Class Module CUserMeasureSortedIndices" End Function '**** Utility Sub's ****** Private Function fGetOurSortedByMeasure(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ Optional ByVal bShowErrorMsg = True) As McRegionLib.McMeasure Dim SortedByMeasure As McRegionLib.McMeasure If Not MeasurementObject Is Nothing Then On Error Resume Next Set SortedByMeasure = MeasurementObject.UserData 'will throw Error if not set On Error GoTo fGetOurSortedByMeasure_Error If SortedByMeasure Is Nothing And bShowErrorMsg Then MsgBox MeasurementObject.Attributes(mcmaidName) + _ " does not have a McMeasure object set as its UserData property." End If 'cannot get a McMeasure from the UserData End If 'we have a MeasurementObject 'else just return Nothing without complaining Set fGetOurSortedByMeasure = SortedByMeasure On Error GoTo 0 Exit Function fGetOurSortedByMeasure_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure fGetOurSortedByMeasure of Class Module CUserMeasureSortedIndices" End Function 'fGetOurSortedByMeasure Private Function fAnalyseOurName(ByVal MeasurementObject As McRegionLib.IMcMeasure, _ ByRef strSortMeasure As String) As mcMeasurementParent On Error GoTo fAnalyseOurName_Error 'get our connected McMeasure's name Dim strOurName As String, strTrailer As String If MeasurementObject Is Nothing Then strSortMeasure = "" 'don't know the sort measurement name fAnalyseOurName = mcmpDescendentOfFeatures 'assume it will work with any McFeatures ancestor Exit Function 'and quit End If 'no MeasurementObject 'ELSE we do have a MeasurementObject strOurName = MeasurementObject.Attributes(mcmaidName) 'Figure if it has one of our special leaders Dim nNameStart As Long, nNameLen As Long, nSBLen As Long nSBLen = Len(strOurName) - Len("SortedBy") If InStr(1, strOurName, "miPt", vbTextCompare) = 1 Then 'Points? nNameStart = InStr(1, strOurName, "SortedBy", vbTextCompare) If nNameStart > 0 Then nNameLen = nSBLen - nNameStart + 1 strTrailer = Right(strOurName, nNameLen) strSortMeasure = "mPt" + strTrailer Else strSortMeasure = "" 'don't know the sort measurement name End If 'have a name fAnalyseOurName = mcmpChildOfPoints Exit Function End If 'a point measurement If InStr(1, strOurName, "miLn", vbTextCompare) = 1 Then 'Lines? nNameStart = InStr(1, strOurName, "SortedBy", vbTextCompare) If nNameStart > 0 Then nNameLen = nSBLen - nNameStart + 1 strTrailer = Right(strOurName, nNameLen) strSortMeasure = "mLn" + strTrailer Else strSortMeasure = "" 'don't know the sort measurement name End If 'have a name fAnalyseOurName = mcmpChildOfLines Exit Function End If 'a line measurement If InStr(1, strOurName, "miRgn", vbTextCompare) = 1 Then 'Regions? nNameStart = InStr(1, strOurName, "SortedBy", vbTextCompare) If nNameStart > 0 Then nNameLen = nSBLen - nNameStart + 1 strTrailer = Right(strOurName, nNameLen) strSortMeasure = "mRgn" + strTrailer Else strSortMeasure = "" 'don't know the sort measurement name End If 'have a name fAnalyseOurName = mcmpChildOfRegions Exit Function End If 'a region measurement If InStr(1, strOurName, "miRef", vbTextCompare) = 1 Then 'Reference features? nNameStart = InStr(1, strOurName, "SortedBy", vbTextCompare) If nNameStart > 0 Then nNameLen = nSBLen - nNameStart strTrailer = Right(strOurName, nNameLen) strSortMeasure = "mRef" + strTrailer Else strSortMeasure = "" 'don't know the sort measurement name End If 'have a name fAnalyseOurName = mcmpChildOfRefFeatures Exit Function End If 'a reference features measurement 'ELSE some other kind of measurement name strSortMeasure = "" 'don't know the sort measurement name fAnalyseOurName = mcmpDescendentOfFeatures 'so it will work with any McFeatures ancestor On Error GoTo 0 Exit Function fAnalyseOurName_Error: MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure fAnalyseOurName of Class Module CUserMeasureSortedIndices" End Function 'fAnalyseOurName