How To ... Develop A Face Area XView Checker - Part 2

How To > Develop A Face Area xView Checker - Part 2

The following tutorial extends the scripted xView Check created in Part 1 with custom text and display overrides and more test options.

The xView Checker is available in 3ds Max 2010 and higher.

Related topics:

xViewChecker Access

Interface:xViewChecker

NATURAL LANGUAGE

Add new mode property and an array of display colors, one for each mode

Extend the xView checker function with new comparison modes

Add a dropdownlist to the dialog to control the new modes

Define a text override function

Define a display override function

Register xViewChecker with new functions.

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
        maxviews redraw
      )
    )
    on ddl_FaceAreaMode selected itm do
    (
      FaceAreaChecker.FaceAreaMode = itm
      updateDisplay()
      clr_resultsColor.color = FaceAreaChecker.resultColors[itm]
    )
    on clr_resultsColor changed val do
    (
      FaceAreaChecker.resultColors[ddl_FaceAreaMode.selection] = val
      updateDisplay()
    )
    on chk_InteractiveUpdates changed state do
    (
      FaceAreaChecker.InteractiveUpdates = state
      updateDisplay()
    ) 
    on spn_FaceAreaThresholdMin changed val do
    (
      FaceAreaChecker.FaceAreaThresholdMin = val
      updateDisplay()
    ) 
    on spn_FaceAreaThresholdMax changed val do
    (
      FaceAreaChecker.FaceAreaThresholdMax = val
      updateDisplay()
    )   
    on btn_selectResults pressed do
    (
      XViewChecker.selectResults CurrentTime
      maxviews 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 (for o in theResults collect o+1)
)
)--end struct
 
try(destroyDialog FaceAreaChecker.faceAreaDialog)catch()
FaceAreaChecker = FaceAreaCheckerStruct()
 
XViewChecker.unRegisterChecker "Face Area Checker"
XViewChecker.registerChecker FaceAreaChecker.geomCheck FaceAreaChecker.supportedCheck #Faces "Face Area Checker" FaceAreaChecker.configDlg FaceAreaChecker.textOverride FaceAreaChecker.dispOverride
)--end script 

Step-By-Step

--code in italic has not changed since Part 1 
(
global FaceAreaChecker
struct FaceAreaCheckerStruct
(
FaceAreaThresholdMin = 1,
FaceAreaThresholdMax = 10, FaceAreaMode = 1,

We will introduce six different modes to the checker, so we will need a property to store the current mode.

faceAreaDialog = undefined, InteractiveUpdates = true,

A new property will control whether the changes to the UI controls will trigger an update to the checker and the viewports. When working with heavy meshes, it might be a good idea to disable the updates.

iniFileName = (getDir #plugcfg +"\\FaceAreaXViewChecker.ini"),

We will store the last known location of the configuration dialog in an INI file.

resultColors = #(green, blue+green*0.5, red+yellow*0.5, blue, yellow, red),

Each check mode will display its results in a different color via the display override function we will implement in this step. This property contains an array of the six colors to use for display. Note that we can create cyan by adding 50% green to blue and orange by adding 50% yellow to red. We could use the predefined global 'orange' to do the same, but this way we have better control over the shade of the color, as we could easily change it to red+yellow*0.75 to make it brighter. There is no predefined global for cyan.

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
) 

We extend the checker with 6 modes to compare the face area values relatively to each of the thresholds. The default mode is the original one - we flag faces whose area lies between the two thresholds. The other modes will flag if the area is blow the minimum, above the minimum but ignoring the maximum, below the maximum but ignoring the minimum, above the maximum and not between the thresholds (either below minimum or above maximum).

) 
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

This drop-down list exposes the 6 checker modes to the configuration dialog. We initialize it to the current value of the struct instance's property.

colorpicker clr_resultsColor fieldwidth:10 align:#right offset:[7,-3] color:FaceAreaChecker.resultColors[FaceAreaChecker.FaceAreaMode]

Next to the drop-down list, we place a small color picker to control the color for the current mode.

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

This checkbox will control the interactive behavior of the tool.

button btn_selectResults "Selection" align:#right width:72 height:18 offset:[7,-3]

This button will perform the conversion of checker flags to sub-object selection on the stack directly from the dialog without the need to navigate to the checker menus.

fn updateDisplay =
( if FaceAreaChecker.InteractiveUpdates do ( XViewChecker.runCheck CurrentTime
max views redraw )

To implement the interactive/non-interactive mode, we simply add a test to see whether the property is true or false. If it is false, we just don't update the test and viewports.

) on ddl_FaceAreaMode selected itm do
(
 FaceAreaChecker.FaceAreaMode = itm
 updateDisplay()
 clr_resultsColor.color = FaceAreaChecker.resultColors[itm]
)

When the drop-down list selection has been changed, we store the new selection index in the new property in the struct instance, update the display and also update the color picker to the color matching the current mode.

on clr_resultsColor changed val do
(
 FaceAreaChecker.resultColors[ddl_FaceAreaMode.selection] = val
 updateDisplay()
)

If the color picker was changed, we update the current color inside the array in the struct instance and also update the display.

on chk_InteractiveUpdates changed state do
(
 FaceAreaChecker.InteractiveUpdates = state
 updateDisplay()
) 

If the interactive mode was changed, we store the current state in the struct instance's property and update the display.

on spn_FaceAreaThresholdMin changed val do
(
 FaceAreaChecker.FaceAreaThresholdMin = val
 updateDisplay()
) 
on spn_FaceAreaThresholdMax changed val do
(
 FaceAreaChecker.FaceAreaThresholdMax = val
 updateDisplay()
)   
 on btn_selectResults pressed do
(
 XViewChecker.selectResults CurrentTime
 max views redraw
) 

If the new button was pressed, we call the method for selecting the results at the current time. This method is implemented by the XView Checker interface and we don’t have to do anything more except for redrawing the viewports to actually see the changes in the selection.

on faceAreaDialog moved pos do setIniSetting FaceAreaChecker.iniFileName "Dialog" "Position" (pos as string)

If the user modes the configuration dialog, we store the new position in the INI file.

)--end rollout
  
local thePos = execute (getIniSetting FaceAreaChecker.iniFileName "Dialog" "Position")

Before opening the dialog, we read the last known position from the INI file.

if classof thePos != Point2 do thePos = mouse.screenpos

If the value returned is not an actual position value (meaning that the INI file has not been created yet or was deleted), we assume the screen position of the mouse.

createDialog faceAreaDialog170 82 thePos.x thePos.y 

We open the dialog at the last known position or the mouse position. We also provide the exact size of the dialog in the first two arguments.

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
)
),

The text override function expects no arguments and returns a string to be displayed in the viewport next to the name of the current Checker. In our case, we just return the current mode and relevant threshold values.

fn dispOverride theTime theNode theHwnd theResults=
(
local theColor = FaceAreaChecker.resultColors[FaceAreaChecker.FaceAreaMode]
XViewChecker.displayResults theColor theTime theNode theHwnd 3 theResults
)

The display override function can be used to implement a user-defined display for the Checker. It expects four arguments, the time, the node, the window handle of the current viewport and the results array which was generated by the checker function.

We call the displayResults method in the XView Checker interface with 6 arguments - the color to display the result in based on the array of colors and the current mode, the time, node and window handle as passed to our function, the type of display (3 for faces) and the array of results.

)--end struct try (destroyDialog FaceAreaChecker.faceAreaDialog)catch()
FaceAreaChecker = FaceAreaCheckerStruct()
 
xViewChecker.unRegisterChecker "Face Area Checker"
xViewChecker.registerChecker FaceAreaChecker.geomCheck FaceAreaChecker.supportedCheck #Faces "Face Area Checker" FaceAreaChecker.configDlgFaceAreaChecker.textOverride FaceAreaChecker.dispOverride

When registering the Checker, we have to add the two new functions for text and display overrides.

)--end script

Using the Script

Our Face Area xView Checker has all checking modes, but its settings are not sticky. In the next Part 3 of the Tutorial, we will look adding sticky settings using an INI file.

To use the new checker, once again click on the Shading viewport menu (the third from the left) and under xView, select any of the standard xView Checkers. Then click in the checker name at the bottom of the viewport and select the Face Area instead. We will learn how to register our xView checker with the Viewport Menus in Part 4 of this tutorials.

To open the Configuration dialog and switch the various modes, click the [Click Here To Configure] label at the bottom of the viewport.

Here is an example of a Teapot with radius 40 and 4 segments collapsed to Editable Poly with all six modes of the Face Area xView checker applied to it:

Viewport xView Checker Results

Configuration Dialog

Mode

 

 

 

 

 

 

Polygons with area from 20 to 108 square units.

 

 

 

 

 

 

Polygons with area less than 20 square units

 

 

 

 

 

 

Polygons with area greater than 20 square units

 
 

 

 

 

Polygons with area less than 108 square units

 

 

 

 

 

 

Polygons with area greater than 108 square units

 

 

 

 

 

 

Polygons with area less than 20 or greater than 108 square units

Previous

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

Next

How To ... Develop A Face Area XView Checker - Part 3

See Also