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.
Related topics:
NATURAL LANGUAGE
Add saving of each configuration dialog setting to the INI file.
Create a list of all settings to be loaded from the INI file.
Load each setting from the INI file when the script is evaluated.
SCRIPT:
( 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) 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) ) on chk_InteractiveUpdates changed state do ( FaceAreaChecker.InteractiveUpdates = state updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "InteractiveUpdates" (state as string) ) on spn_FaceAreaThresholdMin changed val do ( FaceAreaChecker.FaceAreaThresholdMin = val updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "FaceAreaThresholdMin" (val as string) ) on spn_FaceAreaThresholdMax changed val do ( FaceAreaChecker.FaceAreaThresholdMax = val updateDisplay() setIniSetting FaceAreaChecker.iniFileName "Settings" "FaceAreaThresholdMax" (val as string) ) 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 = #( #("Settings","FaceAreaMode", &FaceAreaChecker.FaceAreaMode,0), #("Settings","InteractiveUpdates", &FaceAreaChecker.InteractiveUpdates,0), #("Settings","FaceAreaThresholdMin", &FaceAreaChecker.FaceAreaThresholdMin,0), #("Settings","FaceAreaThresholdMax", &FaceAreaChecker.FaceAreaThresholdMax,0), #("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) ) for i in valuesToLoad do ( local theVal = execute (getIniSetting FaceAreaChecker.iniFileName i[1] i[2]) if theVal != OK do if i[4] > 0 then i[3][i[4]] = theVal else *i[3] = theVal )--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
--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 means that we do not 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 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 do not need to use by-reference here because the property is an array and arrays are always handled by-reference implicitly.
)
for i in valuesToLoad do
(
We loop through the list of settings to load.
local theVal = execute (getIniSetting FaceAreaChecker.iniFileName i[1] i[2])
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 theVal != OK do
if i[4] > 0 then i[3][i[4]] = theVal else *i[3] = theVal
If the result of the evaluation is not the value OK (which is returned if the string returned by the getIniSetting is 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.
Previous
How To ... Develop A Face Area XView Checker - Part2
Next