[Closed] select polys by angle function?
is there a maxscript function to select polys in and Editable_Poly by angle like using the interface. Ideally it would work something like:
getFacesByAngle <integer>faceIndex <float>angleThreshold
does it exist and I just can’t find it in the reference?
Hi Joel,
the following function selects every face in an Editable Poly whose normal is within the specified angle threshold expressed in degrees. It returns a BitArray with bit set for matching faces. It doesn’t consider continuity between faces, so there could be separated groups. To do that would require a littler more effort and a bit slower algorithm. If you plan to use this multiple times on a fixed selection, like varying the threshold with a spinner to see effects in realtime, it would be better to fill an array with face normals and then perform test on stored values rather than querying the Editable Poly each time.
Uhm… I just realized I didn’t look in the MaxScript reference.
function getFacesByAngle oPoly iFace fDegThresh =
(
if ((classOf oPoly) != Editable_Poly) then
throw "Wrong input in function getFacesByAngle() - Editable Poly"
if ((classOf iFace) != Integer) then
throw "Wrong input in function getFacesByAngle() - Face Index"
if ( (iFace <= 0) or (iFace > (polyOp.getNumFaces oPoly)) ) then
throw "Wrong input in function getFacesByAngle() - Face Index out of boundaries"
if ( (classOf fDegThresh != Float) and (classOf fDegThresh != Integer) ) then
throw "Wrong input in function getFacesByAngle() - Angle Threshold"
-- I've become a little precautionary about errors,
-- remove all the previous if not necessary
local p3FaceNormal = polyOp.getFaceNormal oPoly iFace
local fTestDot = cos fDegThresh
local iNumFaces = polyOp.getNumFaces oPoly
local baSelFaces = #{1..iNumFaces}
for i = 1 to iNumFaces do
(
p3ItemNormal = polyOp.getFaceNormal oPoly i
if ((dot p3FaceNormal p3ItemNormal) < fTestDot) then
baSelFaces[i] = false
)
( -- just for visual feedback, remove if unneeded
polyOp.setFaceSelection oPoly baSelFaces
forceCompleteRedraw()
)
return baSelFaces
)
- Enrico
aw :\
fn getFacesByAngle obj faceIndex angleThreshold:10.0 ignoreBackfacing:true = (
local numFaces = obj.numFaces
local faceNormals = for i = 1 to numFaces collect (
polyOp.getFaceNormal obj i
)
local normalAround = faceNormals[faceIndex]
local angleDot = cos(angleThreshold)
local matchFaces
if (ignoreBackFacing) then (
matchFaces = for i = 1 to numFaces collect (
if ((dot normalAround faceNormals[i]) >= angleDot) then ( i )
else ( dontcollect )
)
)
else (
matchFaces = for i = 1 to numFaces collect (
if ((abs (dot normalAround faceNormals[i])) >= angleDot) then ( i )
else ( dontcollect )
)
)
)
Richard
just a bunch of ticks before you, but mine doesn’t take into account backfacing, so it’s a tie
thanks guys, that dot product thing is a better idea than what I was using (acos (dot normalize vec1 normalize vec2))
I was after something that does consider continuity between faces which also means backfacing is not required. I wrote this based off you’re responses:
fn getFacesByAngle obj faceIndex angleThreshold:20 restoreSelection:true =
(
local numFaces = obj.numfaces
local faceNormals = for i = 1 to numFaces collect polyOp.getFaceNormal obj i
local normalAround = faceNormals[faceIndex]
local angleDot = cos angleThreshold
local oldFaceSel = if restoreSelection then polyOp.getFaceSelection obj else undefined
local grow = obj.EditablePoly.GrowSelection
local matchFaces = #(faceIndex)
local oldCount = matchFaces.count
local notFinished = true
polyOp.setFaceSelection obj faceIndex
while notFinished do
(
if keyboard.escPressed do throw " ESCAPE KEY PRESSED " -- just in case (thanks Richard for this from another thread)
grow selLevel:#Face -- grow the face selection
matchFaces = (polyop.getFaceSelection obj) as array
matchFaces = for f in matchFaces where (dot normalAround faceNormals[f]) >= angleDot collect f
if matchFaces.count != oldCount then oldCount = matchFaces.count else notFinished = false
)
if restoreSelection do polyOp.setFaceSelection obj oldFaceSel
matchFaces
)
Hi Joel,
I just upgraded the function to take into account face contiguity. You can now choose between:
#none -> no contiguity limits
#byVert -> faces must be contiguous for (share) at least a vertex
#byEdge -> faces must be contiguous for (share) at least an edge
function getFacesByAngle oPoly iFace fDegThresh contig:#none =
(
if ((classOf oPoly) != Editable_Poly) then
throw "Wrong input in function getFacesByAngle() - Editable Poly"
if ((classOf iFace) != Integer) then
throw "Wrong input in function getFacesByAngle() - Face Index"
if ( (iFace <= 0) or (iFace > (polyOp.getNumFaces oPoly)) ) then
throw "Wrong input in function getFacesByAngle() - Face Index out of boundaries"
if ( (classOf fDegThresh != Float) and (classOf fDegThresh != Integer) ) then
throw "Wrong input in function getFacesByAngle() - Angle Threshold"
if ( (fDegThresh < 0) or (fDegThresh > 180) ) then
throw "Wrong input in function getFacesByAngle() - Angle Threshold out of boundaries"
-- remove all the previous test if not necessary
local p3FaceNormal = polyOp.getFaceNormal oPoly iFace
local fTestDot = cos fDegThresh
local iNumFaces = polyOp.getNumFaces oPoly
if (contig == #none) then
(
local baSelFaces = #{1..iNumFaces}
for i = 1 to iNumFaces do
(
p3ItemNormal = polyOp.getFaceNormal oPoly i
if ((dot p3FaceNormal p3ItemNormal) < fTestDot) then
baSelFaces[i] = false
)
)
else if (contig == #byVert) then
(
local baLimitVerts = #{}
local baLimitFaces = #{iFace}
local baSelFaces = #{iFace}
local baUnselFaces = #{}
while (baLimitFaces.numberSet > 0) do
(
baLimitVerts = (polyOp.getVertsUsingFace oPoly baSelFaces) - (polyOp.getVertsUsedOnlyByFaces oPoly baSelFaces)
baLimitFaces = (polyOp.getFacesUsingVert oPoly baLimitVerts) - baSelFaces - baUnselFaces
for item in baLimitFaces do
(
p3ItemNormal = polyOp.getFaceNormal oPoly item
if ((dot p3FaceNormal p3ItemNormal) >= fTestDot) then
(
baSelFaces[item] = true
)
else
(
baUnselFaces[item] = true
)
)
)
)
else if (contig == #byEdge) then
(
local iNumFaces = polyOp.getNumFaces oPoly
local baLimitVerts = #{}
local baLimitFaces = #{iFace}
local baSelFaces = #{iFace}
local baUnselFaces = #{}
local baEdgeSet01 = #{}
local baFaceSet01 = #{}
local baEdgeSet02 = #{}
while (baLimitFaces.numberSet > 0) do
(
baEdgeSet01 = polyOp.getEdgesUsingFace oPoly baSelFaces
baFaceSet01 = #{1..iNumFaces} - baSelFaces
baEdgeSet02 = polyOp.getEdgesUsingFace oPoly baFaceSet01
baLimitEdges = baEdgeSet01 * baEdgeSet02
baLimitFaces = (polyOp.getFacesUsingEdge oPoly baLimitEdges) - baSelFaces - baUnselFaces
for item in baLimitFaces do
(
p3ItemNormal = polyOp.getFaceNormal oPoly item
if ((dot p3FaceNormal p3ItemNormal) >= fTestDot) then
(
baSelFaces[item] = true
)
else
(
baUnselFaces[item] = true
)
)
)
)
( -- just for visual feedback, remove if unneeded
polyOp.setFaceSelection oPoly baSelFaces
forceCompleteRedraw()
)
return baSelFaces
)
- Enrico
Thanks Enrico
Your #byVertex method is nice and short and works correctly although I dont quite understand why… The method I posted sometimes returns some odd results so i’m using yours. Thanks again