How To > Develop A Face Area xView Checker - Part 3 |
The following tutorial extends the scripted xView Check created in Part 1 and 2 with sticky settings using an INI file.
The xView Checker is available in 3ds Max 2010 and higher.
--code in italic has not changed since Part 2 ( global FaceAreaChecker struct FaceAreaCheckerStruct ( FaceAreaThresholdMin = 1, FaceAreaThresholdMax = 10, FaceAreaMode = 1, faceAreaDialog = undefined, InteractiveUpdates = true, iniFileName = (getDir #plugcfg +"\\FaceAreaXViewChecker.ini"), resultColors = #(green, blue+green*0.5, red+yellow*0.5, blue, yellow, red), fn geomCheck theTime theNode theResults = ( local theCount = case classof theNode of ( Editable_Poly: getNumFaces theNode Editable_Mesh: theNode.numfaces ) local theAreaMethod = case classof theNode of ( Editable_Poly: polyOp.getFaceArea Editable_Mesh: meshOp.getFaceArea ) for f = 1 to theCount do ( local theArea = theAreaMethod theNode f case FaceAreaChecker.FaceAreaMode of ( default: if theArea >= FaceAreaChecker.FaceAreaThresholdMin and theArea <= FaceAreaChecker.FaceAreaThresholdMax do append theResults f 2: if theArea <= FaceAreaChecker.FaceAreaThresholdMin do append theResults f 3: if theArea >= FaceAreaChecker.FaceAreaThresholdMin do append theResults f 4: if theArea <= FaceAreaChecker.FaceAreaThresholdMax do append theResults f 5: if theArea >= FaceAreaChecker.FaceAreaThresholdMax do append theResults f 6: if theArea <= FaceAreaChecker.FaceAreaThresholdMin or theArea >= FaceAreaChecker.FaceAreaThresholdMax do append theResults f ) ) 3 ), fn supportedCheck theNode = ( classof theNode == Editable_Mesh or classof theNode == Editable_Poly ), fn configDlg = ( try (destroyDialog FaceAreaChecker.faceAreaDialog)catch() rollout faceAreaDialog "Face Area Checker" ( dropdownlist ddl_FaceAreaMode width:150 align:#left offset:[-10,-3] items:#("Between Min. and Max","Less Than Min.", "Greater Than Min.","Less Than Max.", "Greater Than Max.","Not Between Min. and Max") selection:FaceAreaChecker.FaceAreaMode across:2 colorpicker clr_resultsColor fieldwidth:10 align:#right offset:[7,-3] color:FaceAreaChecker.resultColors[FaceAreaChecker.FaceAreaMode] spinner spn_FaceAreaThresholdMin "Min. Threshold:" range:[0,1000000,FaceAreaChecker.FaceAreaThresholdMin] offset:[7,-3] type:#worldUnits spinner spn_FaceAreaThresholdMax "Max. Threshold:" range:[0,1000000,FaceAreaChecker.FaceAreaThresholdMax] offset:[7,-3] type:#worldUnits checkbox chk_InteractiveUpdates "Interactive" checked:FaceAreaChecker.InteractiveUpdates offset:[0,-3] across:2 button btn_selectResults "Selection" align:#right width:72 height:18 offset:[7,-3] fn updateDisplay = ( if FaceAreaChecker.InteractiveUpdates do ( XViewChecker.runCheck CurrentTime max views redraw ) ) on ddl_FaceAreaMode selected itm do ( FaceAreaChecker.FaceAreaMode = itm updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "FaceAreaMode" (itm as string)
When the mode is changed, we now save the current selection in the INI file under the category "Settings" and the key "FaceAreaMode".
clr_resultsColor.color = FaceAreaChecker.resultColors[itm] ) on clr_resultsColor changed val do ( FaceAreaChecker.resultColors[ddl_FaceAreaMode.selection] = val updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Colors" ("Color_"+ddl_FaceAreaMode.selection as string) (val as string)
When the color is changed, we now save the new color in the INI file under the category "Colors" and the key "Color_N" where N is the current mode.
) on chk_InteractiveUpdates changed state do ( FaceAreaChecker.InteractiveUpdates = state updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "InteractiveUpdates" (state as string)
When the interactive mode checkbox is changing state, we save the current state in the INI file under the category "Settings" and the key "InteractiveUpdates".
) on spn_FaceAreaThresholdMin changed val do ( FaceAreaChecker.FaceAreaThresholdMin = val updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "FaceAreaThresholdMin" (val as string)
When the min. threshold spinner is changing value, we save the current value in the INI file under the category "Settings" and the key "FaceAreaThresholdMin".
) on spn_FaceAreaThresholdMax changed val do ( FaceAreaChecker.FaceAreaThresholdMax = val updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "FaceAreaThresholdMax" (val as string)
When the min. threshold spinner is changing value, we save the current value in the INI file under the category "Settings" and the key "FaceAreaThresholdMax".
) on btn_selectResults pressed do ( XViewChecker.selectResults CurrentTime max views redraw ) on faceAreaDialog moved pos do setIniSetting FaceAreaChecker.iniFileName "Dialog" "Position" (pos as string) )--end rollout local thePos = execute (getIniSetting FaceAreaChecker.iniFileName "Dialog" "Position") if classof thePos != Point2 do thePos = mouse.screenpos createDialog faceAreaDialog 170 82 thePos.x thePos.y FaceAreaChecker.faceAreaDialog = faceAreaDialog ), fn textOverride = ( case FaceAreaChecker.FaceAreaMode of ( 1: "Between "+FaceAreaChecker.FaceAreaThresholdMin as string +" and " +FaceAreaChecker.FaceAreaThresholdMax as string 2: "Less Than "+FaceAreaChecker.FaceAreaThresholdMin as string 3: "Greater Than "+FaceAreaChecker.FaceAreaThresholdMin as string 4: "Less Than "+FaceAreaChecker.FaceAreaThresholdMax as string 5: "Greater Than "+FaceAreaChecker.FaceAreaThresholdMax as string 6: "Not Between "+FaceAreaChecker.FaceAreaThresholdMin as string +" and " +FaceAreaChecker.FaceAreaThresholdMax as string ) ), fn dispOverride theTime theNode theHwnd theResults= ( local theColor = FaceAreaChecker.resultColors[FaceAreaChecker.FaceAreaMode] XViewChecker.displayResults theColor theTime theNode theHwnd 3 theResults ) )--end struct try (destroyDialog FaceAreaChecker.faceAreaDialog)catch() FaceAreaChecker = FaceAreaCheckerStruct() local valuesToLoad = #(
To simplify the loading of the INI file settings, we declare an array of arrays to define the category, key, property to set and in the case of colors, the index of the color.
#("Settings", "FaceAreaMode", &FaceAreaChecker.FaceAreaMode,0), #("Settings", "InteractiveUpdates", &FaceAreaChecker.InteractiveUpdates,0), #("Settings", "FaceAreaThresholdMin", &FaceAreaChecker.FaceAreaThresholdMin,0), #("Settings", "FaceAreaThresholdMax", &FaceAreaChecker.FaceAreaThresholdMax,0),
To load these settings, we define an array containing the category name, the key name, a reference to the property and the index 0 which will mean we don't use sub-indexing. We take the property by-reference with the & operator to store the pointer to the property instead of its value. This way, the property is not evaluated and we can write back a value into into it using its pointer and the de-referencing operator *.
#("Colors", "Color_1", FaceAreaChecker.resultColors, 1), #("Colors", "Color_2", FaceAreaChecker.resultColors, 2), #("Colors", "Color_3", FaceAreaChecker.resultColors, 3), #("Colors", "Color_4", FaceAreaChecker.resultColors, 4), #("Colors", "Color_5", FaceAreaChecker.resultColors, 5), #("Colors", "Color_6", FaceAreaChecker.resultColors, 6)
To load these color settings, we define an array containing the category name, the key name, a reference to the property and the corresponding color index into the array of colors. We don't need to use by-reference here because the property is an array and arrays are always handled by-reference implicitly.
We loop through the list of settings to load.
We load the value from the INI file based on the category and the key name and evaluate the string to convert to the correct MAXScript value.
If the result of the evaluation is not the value OK (which would be returned if the string returned by the getIniSetting were empty), we check to see whether the 4 th element of the sub-array is greater than zero. If it is, we assign the value to the corresponding indexed item of the colors array. Otherwise, we just assign the value to the de-referenced property.
)--end i loop xViewChecker.unRegisterChecker "Face Area Checker" xViewChecker.registerChecker FaceAreaChecker.geomCheck FaceAreaChecker.supportedCheck #Faces "Face Area Checker" FaceAreaChecker.configDlg FaceAreaChecker.textOverride FaceAreaChecker.dispOverride )--end script
Our Face Area xView Checker is now feature-complete. In the last Part 4 of the tutorial, we will look at how it can be integrated into the Viewport Menus for easier access.
The settings in the Configuration Dialog will now be sticky between sessions. The rest of the features are the same as in the previous Part 2.