A point with “valence 2” is a point with two incident (neighbour) edges. You could write a script to find these points, but it’s even easier to do with ICE:

Notice that some of the tagged points look like they have more than 2 neighbour edges. What’s happening there is that there are several vertices on top of each other:

Here’s a Python script that builds the ICE tree and then uses it to select all the interior points with valence 2.
hat tip: Fabricio Chamon
I also did this as a custom filter, maybe I’ll post that later.
from siutils import si
from siutils import log # LogMessage
from siutils import disp # win32com.client.Dispatch
from siutils import C # win32com.client.constants
#
# Build the ICE tree that finds the interior points with valence two
#
def BuildICETree( oObject ):
oIceTree = si.ApplyOp("ICETree", oObject.FullName, C.siNode, "", "", 0)(0)
oIceTree.Name = "PS_ValenceTwoFilter"
oAnd = si.AddICENode("$XSI_DSPRESETS\\ICENodes\\CombineLogicNode.Preset", oIceTree )
#
# Get self.VertexIsCorner -> Not -> And
#
oNot = si.AddICENode("$XSI_DSPRESETS\\ICENodes\\NotNode.Preset", oIceTree )
oGetVertexIsCorner = si.AddICENode("$XSI_DSPRESETS\\ICENodes\\GetDataNode.Preset", oIceTree )
oGetVertexIsCorner.Parameters( "Reference" ).Value = "self.VertexIsCorner"
si.ConnectICENodes( oNot.InputPorts("Value"), oGetVertexIsCorner.OutputPorts( "value" ) )
si.ConnectICENodes( oAnd.InputPorts( "Value1" ), oNot.OutputPorts("result") );
#
# Get self.VertexToEdges -> Get Array Size -> = -> And
#
oGetVertexToEdges = si.AddICENode("$XSI_DSPRESETS\\ICENodes\\GetDataNode.Preset", oIceTree )
oGetVertexToEdges.Parameters( "Reference" ).Value = "self.VertexToEdges"
oArraySize = si.AddICENode("$XSI_DSPRESETS\\ICENodes\\GetArraySizeNode.Preset", oIceTree )
oCompare = si.AddICENode("$XSI_DSPRESETS\\ICENodes\\CompareNode.Preset", oIceTree )
si.ConnectICENodes( oArraySize.InputPorts("Array"), oGetVertexToEdges.OutputPorts("value") )
si.ConnectICENodes( oCompare.InputPorts("first"), oArraySize.OutputPorts("size") )
oCompare.InputPorts("second").Value = 2
si.AddPortToICENode( oAnd.InputPorts("Value1"), "siNodePortDataInsertionLocationAfter")
si.ConnectICENodes( oAnd.InputPorts("Value2"), oCompare.OutputPorts("result") )
#
# Set Data -> ICETree
#
oSetData = si.AddICECompoundNode("Set Data", oIceTree )
si.SetValue( oSetData.FullName + ".Reference", "self._PsValenceTwoFlag", "")
si.ConnectICENodes( oSetData.InputPorts("Value"), oAnd.OutputPorts( "result" ) )
si.ConnectICENodes( oIceTree.InputPorts("port1"), oSetData.OutputPorts("Execute") )
si.DisplayPortValues(oSetData.InputPorts( "Value" ), True, 0, True, "", 0, 0, 0, 1, False, True, 1, 0.5, 0, 1, False, 0, 10000, 1, False, False, 0, 10, False, True, False, 100)
return oIceTree
#
# Select all points with the ICE attribute _PsValenceTwoFlag=True
#
def SelectInteriorPoints_with_ValenceTwo( oObject ):
a = oObject.ActivePrimitive.ICEAttributes("_PsValenceTwoFlag")
if a is not None:
d = a.DataArray
if len(d) > 0 and a.IsConstant == False:
Application.SelectGeometryComponents( "%s.pnt[%s]" %( oObject.FullName, ",".join(["%s" %(ix) for ix in range(len(d)) if d[ix] == -1]) ) )
#--------------------------------------------------------------
# Select interior points with valence 2
#--------------------------------------------------------------
if si.Selection.Count > 0 and si.ClassName( si.Selection(0) ) != "CollectionItem" :
oObject = si.Selection(0);
else:
oObject = si.PickObject( "Pick object" )(2)
if oObject != None and oObject.IsClassOf( C.siX3DObjectID ):
tree = BuildICETree( oObject )
SelectInteriorPoints_with_ValenceTwo( oObject )
si.DeleteObj( tree )
Excellent Script. I’ve wished for something like this for Softimage for a while. Filter non-manifold geometry would also be helpful script, if possible.
Thank You.