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.

Related topics:

xViewChecker Access

Interface:xViewChecker

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

Using the 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

How To ... Develop A Face Area XView Checker - Part4